stringフォーマッティングは、おそらくPythonであなたが通常することであり、あえて考えたりはしないでしょう。
それはとても簡単で、まさに"%d %d" % (i, i)すれば完了です。
結果のバッファをどのように測るかは考えません。あなたの出力の最後に適したNULLバイトを持っているようと、または他の細かい事があろうと。Cに相当するのは次のようになります:
char x[44];
sprintf(x, "%d %d", i, i);
2行目のところで立ち止まり、数がどれくらい大きくなるかもしれないかを考慮して、サイズを過大評価しなければならなかったことに注意してください。
(44 = 64ビット上で最も大きい数の長さ(20) + 1 (符号のための) * 2 + 1 (空白のための) + 1 (NULバイト))
この投稿の著者、fijalとalexはこの3つの計算の試みを異議なしで実現しました :-)
この関数からxを返すことができない場合を除きすばらしいです。よりよい比較は次のようになります。
char *x = malloc(44 * sizeof(char));
sprintf(x, "%d %d", i, i);
xはいくらかの状況でわずかに過剰割り当てされますが、すばらしいです。
しかし、ただstringフォーマッティングの実現について議論するために、私たちはここいるわけではなく、速いPyPyを明確に新たなunroll-if-altブランチとともに示し、議論するためにここにいます。Pythonは次のコードをもたらします:
def main():
for i in xrange(10000000):
"%d %d" % (i, i)
main()
Cコード:
#include
#include
int main() {
int i = 0;
char x[44];
for (i = 0; i < 10000000; i++) {
sprintf(x, "%d %d", i, i);
}
}
PyPyのunroll-if-alt branchブランチのheadで実行し、コンパイルはGCC4.5.2 -O4(他の最適化レベルをテストし、これが最高のパフォーマンスをもたらした)。PyPyで実行すると0.85秒、コンパイル済みのバイナリで実行すると1.63秒かかりました。
私たちはこれは動的コンパイルの信じられないほどの可能性を示すと考えおり、GCCはsprintfの呼び出しをインライン化またはアンロールすることができません。理由はlibcが内部に位置しているためです。
Cのベンチマークコード:
#include
#include
int main() {
int i = 0;
for (i = 0; i < 10000000; i++) {
char *x = malloc(44 * sizeof(char));
sprintf(x, "%d %d", i, i);
free(x);
}
}
前に議論されたとおり、これはPythonに匹敵し、1.96秒の結果が得られます。
パフォーマンスの結論:
Platform | GCC (stack) | GCC (malloc) | CPython | PyPy (unroll-if-alt) |
---|---|---|---|---|
Time | 1.63s | 1.96s | 10.2s | 0.85s |
relative to C | 1x | 0.83x | 0.16x | 1.9x |
全体的にPyPyは2倍速いです。これは明らかに、静的より動的コンパイラが優っています。 - sprintf関数はlibcにあり、不変な文字列に特化することができず、実行されるときはつねに、文字列を評価しなければなりません。
PyPyのケースでは、私たちは、剰余演算子の左側の文字列が不変であると検出すれば、アセンブラに特化することができます。
Cheers,
alex & fijal
(原文:Posted by Maciej Fijalkowski)
0 件のコメント:
コメントを投稿