Perché Perl's Inline :: C ordina 4.0e-5 dopo 4.4e-5?
Domanda
Ho creato un modulo Perl Inline :: C , ma c'è qualche stranezza con l'ordinamento. Qualcuno sa perché dovrebbe ordinare in questo modo? Perché 4.0e-5 non è il primo?
my $ref = [ 5.0e-5,4.2e-5,4.3e-5,4.4e-5,4.4e-5,4.2e-5,4.2e-5,4.0e-5];
use Inline C => <<'END_OF_C_CODE';
void test(SV* sv, ...) {
I32 i;
I32 arrayLen;
AV* data;
float retval;
SV** pvalue;
Inline_Stack_Vars;
data = SvUV(Inline_Stack_Item(0));
/* Determine the length of the array */
arrayLen = av_len(data);
// sort
sortsv(AvARRAY(data),arrayLen+1,Perl_sv_cmp_locale);
for (i = 0; i < arrayLen+1; i++) {
pvalue = av_fetch(data,i,0); /* fetch the scalar located at i .*/
retval = SvNV(*pvalue); /* dereference the scalar into a number. */
printf("%f \n",newSVnv(retval));
}
}
END_OF_C_CODE
Test ($ ref);
0.000042
0.000042
0.000042
0.000043
0.000044
0.000044
0.000040
0.000050
Soluzione 3
Ricevi una risposta con l'aiuto delle persone all'indirizzo http://www.perlmonks.org/ ? NODE_ID = 761.015
Ho eseguito alcuni profili (DProf) ed è un miglioramento di 4x della velocità
Tempo totale trascorso = 0,543205 secondi
Utente + Ora di sistema = 0,585454 Secondi
Tempi esclusivi
% Time ExclSec CumulS #Call sec / call Csec / c Nome
100. 0,590 0,490 100000 0,0000 0,0000 test_inline_c_pkg :: percent2
Tempo totale trascorso = 2.151647 Secondi
Utente + Ora di sistema = 1.991647 Secondi
Tempi esclusivi
% Time ExclSec CumulS #Call sec / call Csec / c Nome
104. 2.080 1.930 100000 0.0000 0.0000 principale :: percent2
Ecco il codice
use Inline C => <<'END_OF_C_CODE';
#define SvSIOK(sv) ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK)
#define SvNSIV(sv) (SvNOK(sv) ? SvNVX(sv) : (SvSIOK(sv) ? SvIVX(sv) : sv_2nv(sv)))
static I32 S_sv_ncmp(pTHX_ SV *a, SV *b) {
const NV nv1 = SvNSIV(a);
const NV nv2 = SvNSIV(b);
return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
}
void test(SV* sv, ...) {
I32 i;
I32 arrayLen;
AV* data;
float retval;
SV** pvalue;
Inline_Stack_Vars;
data = SvUV(Inline_Stack_Item(0));
/* Determine the length of the array */
arrayLen = av_len(data);
/* sort descending (send numerical sort function S_sv_ncmp) */
sortsv(AvARRAY(data),arrayLen+1, S_sv_ncmp);
for (i = 0; i < arrayLen+1; i++) {
pvalue = av_fetch(data,i,0); /* fetch the scalar located at i .*/
retval = SvNV(*pvalue); /* dereference the scalar into a number. */
printf("%f \n",newSVnv(retval));
}
}
END_OF_C_CODE
Altri suggerimenti
Poiché stai ordinando in modo lessicale, prova questo codice:
#!/usr/bin/perl
use strict;
use warnings;
my $ref = [ 5.0e-5,4.2e-5,4.3e-5,4.4e-5,4.4e-5,4.2e-5,4.2e-5,4.0e-5];
print "Perl with cmp\n";
for my $val (sort @$ref) {
printf "%f \n", $val;
}
print "Perl with <=>\n";
for my $val (sort { $a <=> $b } @$ref) {
printf "%f \n", $val;
}
print "C\n";
test($ref);
use Inline C => <<'END_OF_C_CODE';
void test(SV* sv, ...) {
I32 i;
I32 arrayLen;
AV* data;
float retval;
SV** pvalue;
Inline_Stack_Vars;
data = SvUV(Inline_Stack_Item(0));
/* Determine the length of the array */
arrayLen = av_len(data);
// sort
sortsv(AvARRAY(data),av_len(data)+1,Perl_sv_cmp_locale);
arrayLen = av_len(data);
for (i = 0; i < arrayLen+1; i++) {
pvalue = av_fetch(data,i,0); /* fetch the scalar located at i .*/
retval = SvNV(*pvalue); /* dereference the scalar into a number. */
printf("%f \n",newSVnv(retval));
}
}
END_OF_C_CODE
Naturalmente, lessicamente 0.00040
è anche più piccolo di 0.00042
, ma non stai confrontando 0.00040
con 0.00042
; stai confrontando il numero 0.00040
convertito in una stringa con il numero 0.00042
convertito in una stringa. Quando un numero diventa troppo grande o piccolo, la logica stringente di Perl ricorre all'uso della notazione scientifica. Quindi stai ordinando il set di stringhe
"4.2e-05", "4.2e-05", "4.2e-05", "4.3e-05", "4.4e-05", "4.4e-05", "4e-05", "5e-05"
che sono correttamente ordinati. Perl trasforma felicemente quelle stringhe nei loro numeri quando lo chiedi con il formato % f
in printf
. Potresti stringere i numeri da solo, ma dal momento che hai dichiarato che vuoi che questo sia più veloce, sarebbe un errore. Non dovresti cercare di ottimizzare il programma prima di sapere dove rallenta (l'ottimizzazione prematura è la radice di tutti i malvagi *
). Scrivi il tuo codice quindi esegui Devel :: NYTProf contro di esso per trovare dove è lento . Se necessario, riscrivi quelle parti in XS o Inline :: C
(Preferisco XS). Scoprirai che avrai più velocità nella scelta della giusta struttura di dati rispetto alle micro-ottimizzazioni come questa.
*
Knuth, Donald . Programmazione strutturata con andare a Dichiarazioni , ACM Journal Computing Surveys , volume 6, n. 4, dicembre 1974. p.268.
Perl_sv_cmp_locale
è la tua funzione di ordinamento che sospetto sia un confronto lessicale. Cerca un ordinamento numerico o scrivine uno tuo.