PerlのInline :: Cが4.0e-5を4.4e-5の後にソートするのはなぜですか?
質問
Perl Inline :: C モジュールを作成しましたが、奇妙な点がありますソート付き。なぜこのように分類されるのか誰にもわかりますか? 4.0e-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
解決 3
http://www.perlmonks.org/で、人々の助けを借りて回答を得る?node_id = 761015
プロファイリング(DProf)を実行したところ、速度が4倍向上しました
合計経過時間= 0.543205秒
ユーザー+システム時間= 0.585454秒
限定タイム
%Time ExclSec CumulS #Calls sec / call Csec / c Name
100. 0.590 0.490 100000 0.0000 0.0000 test_inline_c_pkg :: percent2
合計経過時間= 2.151647秒
ユーザー+システム時間= 1.991647秒
限定タイム
%Time ExclSec CumulS #Calls sec / call Csec / c Name
104. 2.080 1.930 100000 0.0000 0.0000 main :: percent2
コードは次のとおりです
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
他のヒント
レキシカルにソートしているため、次のコードを試してください:
#!/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
もちろん、字句的には 0.00040
も 0.00042
よりも小さいですが、 0.00040
と 0.00042 コード>;文字列に変換された数字
0.00040
と文字列に変換された数字 0.00042
を比較しています。数値が大きすぎたり小さすぎたりすると、Perlの文字列化ロジックは科学表記法を使用します。したがって、文字列のセットをソートしています
"4.2e-05", "4.2e-05", "4.2e-05", "4.3e-05", "4.4e-05", "4.4e-05", "4e-05", "5e-05"
適切にソートされています。 Perlは、 printf
の%f
形式で要求すると、それらの文字列を喜んで数字に戻します。自分で数字を文字列化することもできますが、これをもっと高速にしたいと述べているので、それは間違いです。プログラムがどこで遅くなるかを知る前に、プログラムを最適化しようとすべきではありません(早すぎる最適化がすべての悪 *
の根本です)。コードを記述し、 Devel :: NYTProf を実行して、どこが遅いかを見つけます。 。必要に応じて、 XS または Inline :: C
の部分を書き換えます(XSが好きです)。このようなマイクロ最適化よりも、適切なデータ構造を選択するほうがより高速になります。
*
クヌース、ドナルド。 go toステートメントを使用した構造化プログラミング、 ACM Journal Computing Surveys 、Vol 6、No。4、1974年12月。p.268。
Perl_sv_cmp_locale
は、語彙比較と思われるソート関数です。数値の並べ替えを探すか、独自に作成します。