2009-04-29 7 views
3

J'ai construit un module Perl Inline::C, mais il y a une certaine bizarrerie avec le tri. Est-ce que quelqu'un sait pourquoi cela serait comme ça? Pourquoi le 4.0e-5 n'est pas le premier?Pourquoi Perl's Inline :: C sort 4.0e-5 après 4.4e-5?

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

Répondre

1

ont une réponse avec l'aide des personnes de plus à http://www.perlmonks.org/?node_id=761015

J'ai couru un certain profilage (DProf) et il y a une amélioration 4x vitesse

Temps total écoulé = 0.543205 secondes
utilisateur + System Time = 0.585454 secondes
Exclusive temps
% Temps ExclSeC#Calls sec cumuls/appel Nom CSTC/c
100. 0,590 0,490 100000 0,0000 0,0000 test_inline_c_pkg :: cent2

Temps total écoulé = 2,151647 secondes
Temps utilisateur + système = 1.991647 secondes
Exclusive temps
% Temps ExclSeC#Calls sec cumuls/appel CSTC/c Nom
104. 2,080 1,930 100000 0,0000 0,0000 principal :: cent2

Voici le code

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 
4

Parce que vous triez lexicalement, essayez ce code:

#!/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 

Bien sûr, lexicalement 0.00040 est plus petit que 0.00042 aussi bien, mais vous ne comparez pas 0.00040-0.00042; vous comparez le nombre 0.00040 converti en une chaîne avec le nombre 0.00042 converti en une chaîne. Quand un nombre devient trop grand ou trop petit, la logique de l'enchaînement de Perl recourt à la notation scientifique. Donc, vous triez l'ensemble des chaînes

"4.2e-05", "4.2e-05", "4.2e-05", "4.3e-05", "4.4e-05", "4.4e-05", "4e-05", "5e-05" 

qui sont triés correctement. Perl retourne avec bonheur ces chaînes dans leurs nombres lorsque vous le demandez avec le format %f en printf. Vous pouvez vous-même codifier les chiffres, mais puisque vous avez déclaré que vous voulez que ce soit plus rapide, ce serait une erreur. Vous ne devriez pas essayer d'optimiser le programme avant de savoir où il ralentit (l'optimisation prématurée est la racine de tous les maux *). Ecrivez votre code, puis exécutez Devel::NYTProf pour trouver où il est lent. Si nécessaire, réécrire ces parties dans XS ou Inline::C (je préfère XS). Vous constaterez que vous obtenez plus de rapidité dans le choix de la bonne structure de données que des micro-optimisations comme celle-ci.

*Knuth, Donald. Structured Programming with go to Statements, ACM Journal Computing Surveys, vol. 6, n ° 4, décembre 1974. p.268.

+0

I aimerait avoir le tri dans la section Inline :: C pour la vitesse. Cela peut être le problème, mais comment truquer() pour trier numériquement n'est pas bien documenté. –

+6

Vous n'en tirerez aucune vitesse. La fonction de tri en Perl est écrite en C. –

+2

Maintenant, si vous voulez dire la fonction que vous passez à la fonction de tri, cela peut donner un coup de pouce à la vitesse, mais cela devrait être assez compliqué. –

2

Perl_sv_cmp_locale est votre fonction de tri que je soupçonne être une comparaison lexicale. Cherchez le tri numérique ou écrivez le vôtre.

+1

Ceci n'est pas bien documenté dans l'API perl. Même en essayant d'essayer d'écrire ma propre fonction, pas de chance. –