TL、DR;
Cプログラムとして、これはメモリに縛られたアルゴリズムです。しかし、ジットコンパイルされたpypyコードでさえ、かなりのオーバーヘッドを持ち、操作はもはや「無料」ではありません。驚いたことに(またはそうでないかもしれませんが)、2つのsieve
バージョンのジットコードが違っています.2番目のバージョンの方がコードが遅くなるのはおそらく不運です。
もしそれがCであれば、@ Arminの回答は正しいでしょう。最新のコンピュータ/キャッシュとメモリバウンドコードでは、整数にジャンプするかどうかは関係ありませんが、すべての値をメモリからフェッチする必要があり、これはボトルネックであることはよく知られています。詳しい説明はthis articleを参照してください。
私の実験では、最適化されていないバージョン(sieve
)は最適化されたバージョン(sieve_var
)よりわずかに高速です。 sieve
の最後の行がsieve_var
- return [2] + [i for i in range(3, n, 2) if nums[i] == 0]
よりも速く実行されるというタイミングも示されています。
私のマシンでは秒対0.65
秒(10^8
要素)の秒でした。これらの数値はマシンによって異なる可能性があります。したがって、より高速のCPUと低速のメモリを持つ人は、まったく差異が見えない可能性があります。 「メモリ支配」という言葉で説明できれば、より遅いバージョンではより高速なバージョンとしてキャッシュミスが増えることが分かります。
しかし、valgrind --tool=cachegrind pypy sieveXXX.py
を実行することによって、キャッシュミスの数にはほとんど違いがなく、少なくとも観察可能な違いを説明するものはないことがわかります。
のは、まったく同じ挙動を示す少し簡単なバージョンを、考えてみましょう - 私たちは、素数を保存し、ちょうどそれらをカウントされません:
def sieve(n):
...
res=0
for i in range(2, n):
if nums[i] == 0:
res+=1
return res
def sieve_var(n):
...
res=1
for i in range(3, n,2):
if nums[i] == 0:
res+=1
return res
最初のバージョンはより速く、まだです:0.35
秒。対0.45
秒(時間差が偶然ではなく、いくつかのjit-warmupのせいではないことを確かめるために、私はコードの最後の部分をfor-loopに置き、常に同じタイミングを得ました)。さらに進む前に
、のはgcc and -Os
we getでコンパイルされたC-の実装とその組立
long long int sum(long long int *a, int n){
long long int res=0;
for(int i=2;i<n;i++)
if(a[i]==0)
res++;
return res;
}
を見てみましょう:
movl $2, %edx
xorl %eax, %eax
.L4:
cmpl %edx, %esi
jle .L1
cmpq $0, (%rdi,%rdx,8)
jne .L3
incq %rax
.L3:
incq %rdx
jmp .L4
.L1:
ret
まっすぐ進むかなり小さくて、私にだけ0.08
秒かかります機械。私の記憶は10GB/sの速さで、8*10^8
バイトがあるので、データを得るためには基本的に全時間が必要でした。
しかしこれからも、ピーピーバージョンはCコードと比較して約0.25
秒のオーバーヘッドを持つことがわかります。それはどこから来たのですか? vmprof-moduleを利用することにより、我々はJITコードと見ることができます:ループの1回の繰り返しのために
- を、C-バージョン
- でより多くの操作がある
sieve
とsieve_par
用のバージョンは非常に異なっています。反復のuse debugger to count the number of instructionはsieve
のためのと76
の2番目の要素だけを扱うsieve_var
の操作です。実際には、関係は24:38
です。
pypyをデバッグしないと、両方のバージョンでjitコードが大きく異なる理由は分かりません。おそらく、それはちょうど不運である、sieve_par
が遅いです。
@ user2357112もしそれが間違っているなら、 'sieve(10 ** 8)== sieve_var(10 ** 8)'はなぜですか? – qwr
どうやってこれをやったのですか? – user2357112
@ user2357112基本的に 't = time.clock(); print(len(sieve(10 ** 8)))); print(time.clock()-t) ' – qwr