54

結果が意味をなさないので、私はこれを間違って実装したかもしれないと思います。1000000000までカウントするGoプログラムがあります。

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 1000000000; i++ {}
    fmt.Println("Done") 
}

1秒以内に終了します。一方、私はPythonスクリプトを持っています:

x = 0
while x < 1000000000:
    x+=1
print 'Done'

数分で終了します。

Goバージョンがこれほど高速なのはなぜですか?それらは両方とも1000000000までカウントしていますか、それとも私は何かを逃していますか?

4

8 に答える 8

93

10億はそれほど大きな数ではありません。ネイティブタイプで作業を行うことができれば、合理的に最新のマシンであれば、せいぜい数秒でこれを行うことができるはずです。これを確認するには、同等のCプログラムを作成し、アセンブリを読み取って実際に加算が行われていることを確認し、タイミングを調整します(私のマシンでは約1.8秒で完了します)。

ただし、Pythonにはネイティブ型変数(または意味のある型アノテーション)の概念がないため、この場合は数百倍の作業を行う必要があります。要するに、あなたの見出しの質問に対する答えは「はい」です。Goは、副作用のないループを最適化するようなコンパイラのトリックがなくても、Pythonよりもはるかに高速になる可能性があります。

于 2012-09-25T01:35:26.690 に答える
74

pypyは実際にこのループをスピードアップするという印象的な仕事をしています

def main():
    x = 0
    while x < 1000000000:
        x+=1

if __name__ == "__main__":
    s=time.time()
    main()
    print time.time() - s

$ python count.py 
44.221405983
$ pypy count.py 
1.03511095047

〜97%スピードアップ!

「理解できなかった」3人の説明。Python言語自体は遅くありません。CPythonの実装は、コードを実行するための比較的簡単な方法です。Pypyは、大きな違いを生む可能性のある多くのトリッキーな(特にJIT)ことを行う言語の別の実装です。タイトルの質問に直接答える-GoはPythonよりも「それほど」高速ではありません。GoはCPythonよりもはるかに高速です。

そうは言っても、コードサンプルは実際には同じことをしていません。intPythonは、そのオブジェクトの1000000000をインスタンス化する必要があります。Goは、1つのメモリ位置をインクリメントするだけです。

于 2012-09-25T05:43:46.280 に答える
24

このシナリオは、適切なネイティブコンパイルされた静的型付け言語を非常に優先します。ネイティブにコンパイルされた静的型付け言語は、終了に単純なチェック条件を利用する4〜6個のCPUオペコードなどの非常に些細なループを生成できます。このループは事実上分岐予測ミスがゼロであり、CPUサイクルごとに増分を実行するものと効果的に考えることができます(これは完全に真実ではありませんが..)

Pythonの実装では、主に動的型付けのために、かなり多くの作業を行う必要があります。Pythonは、2つを足しint合わせるためだけに、いくつかの異なる呼び出し(内部および外部)を行う必要があります。Pythonでは呼び出す必要__add__があり(事実上i = i.__add__(1)ですが、この構文はPython 3.xでのみ機能します)、渡された値の型をチェックする必要があり(それがであるかどうかを確認するためint)、整数値を追加します(両方のオブジェクトからそれらを抽出します)、次に新しい整数値が新しいオブジェクトに再びラップされます。最後に、新しいオブジェクトをローカル変数に再割り当てします。それはかなり多くの仕事ですインクリメントする単一のオペコードよりも、ループ自体に対処することすらありません-比較すると、Go/nativeバージョンは副作用によってレジスタをインクリメントするだけである可能性があります。

Javaは、このような些細なベンチマークではるかに優れており、Goにかなり近い可能性があります。JITとカウンター変数の静的型付けにより、これを確実に行うことができます(特別な整数のadd JVM命令を使用します)。繰り返しになりますが、Pythonにはそのような利点はありません。現在、静的型付けフェーズを実行し、ここではCPythonよりもはるかにうまくいくはずのPyPy/RPythonのようないくつかの実装があります。

于 2012-09-25T01:17:13.093 に答える
10

ここでは2つのことが機能しています。1つ目は、GoがマシンコードにコンパイルされてCPUで直接実行されるのに対し、Pythonは(特に低速の)VMに対してバイトコードで実行されるようにコンパイルされることです。

パフォーマンスに影響を与える2番目の、そしてより重要なことは、2つのプログラムのセマンティクスが実際には大幅に異なることです。Goバージョンでは、「x」と呼ばれる「ボックス」が作成されます。このボックスには、プログラムを通過するたびに数値が1ずつ増加します。Pythonバージョンは、実際には各サイクルで新しい「ボックス」(intオブジェクト)を作成する必要があります(そして、最終的にはそれらを破棄する必要があります)。プログラムを少し変更することで、これを実証できます。

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d %p\n", i, &i)
    }
}

...と:

x = 0;
while x < 10:
    x += 1
    print x, id(x)

これは、GoがCのルートであるため、場所を参照するために変数名を使用し、Pythonが物事を参照するために変数名を使用するためです。整数はPythonで一意の不変のエンティティと見なされるため、常に新しいエンティティを作成する必要があります。PythonはGoよりも遅いはずですが、最悪のシナリオを選択しました。ベンチマークゲームでは、平均して約25倍(最悪の場合は100倍)高速であることがわかります。

Pythonプログラムが遅すぎる場合は、Cに移動することで速度を上げることができます。幸い、この場合、誰かがすでにこれを行っています。空のループを次のようにxrange()を使用するように書き直す場合:

for x in xrange(1000000000):
    pass
print "Done."

...約2倍の速度で実行されます。ループカウンターが実際にプログラムの主要なボトルネックであることがわかった場合は、問題を解決するための新しい方法を調査する時期かもしれません。

于 2012-09-25T01:34:44.860 に答える
3

@troq

私はパーティーに少し遅れていますが、答えはイエスとノーだと思います。@gnibblerが指摘したように、CPythonは単純な実装では低速ですが、pypyは必要なときにはるかに高速なコード用にjitコンパイルされます。

CPythonで数値処理を行っている場合、ほとんどの場合、numpyで数値処理を行うため、配列と行列の操作が高速になります。最近、私はnumbaを使って多くのことを行っています。これにより、コードに単純なラッパーを追加できます。これについては、上記のコードを実行する関数incALot()に@njitを追加しました。

私のマシンでは、CPythonは61秒かかりますが、numbaラッパーを使用すると7.2マイクロ秒かかります。これは、Cと同様で、Goよりも高速です。これは800万倍のスピードアップです。

したがって、Pythonでは、数値のあるものが少し遅いように思われる場合でも、それに対処するためのツールがあります。それでも、Pythonのプログラマーの生産性とREPLを得ることができます。

def incALot(y):
    x = 0
    while x < y:
        x += 1

@njit('i8(i8)')
def nbIncALot(y):
    x = 0
    while x < y:
        x += 1
    return x

size = 1000000000
start = time.time()
incALot(size)
t1 = time.time() - start
start = time.time()
x = nbIncALot(size)
t2 = time.time() - start
print('CPython3 takes %.3fs, Numba takes %.9fs' %(t1, t2))
print('Speedup is: %.1f' % (t1/t2))
print('Just Checking:', x)

CPython3 takes 58.958s, Numba takes 0.000007153s
Speedup is: 8242982.2
Just Checking: 1000000000
于 2014-10-20T02:52:45.900 に答える
0

問題は、Pythonが解釈されることですが、GOは解釈されないため、テスト速度をベンチに置く実際の方法はありません。インタプリタ言語は通常(常にvmコンポーネントを持っているとは限りません)、問題が発生します。実行するテストはすべて、実際のランタイム境界ではなく、インタプリタ境界で実行されます。Goは、速度の点でCよりもわずかに遅いです。これは主に、手動のメモリ管理の代わりにガベージコレクションを使用しているためです。とはいえ、Pythonと比較してGOはコンパイルされた言語であるため高速ですが、GOに欠けているのはバグテストだけで、間違っている場合は修正されます。

于 2016-07-24T00:42:37.387 に答える
-1

コンパイラは、ループの後に「i」変数を使用していないことに気付いた可能性があるため、ループを削除して最終的なコードを最適化しました。

後で使用したとしても、コンパイラはおそらくループを次のように置き換えるのに十分賢いです

i = 1000000000;

これがお役に立てば幸いです=)

于 2012-09-25T01:22:04.647 に答える
-6

私はgoに精通していませんが、ループの本体は何もしないので、goバージョンはループを無視すると思います。一方、Pythonバージョンでxは、ループの本体でインクリメントしているため、おそらく実際にループを実行しています。

于 2012-09-25T01:03:30.733 に答える