6 ^ 6 ^ 6の評価におけるpython対bc


29

とを個別に6^6^6使用pythonして式を評価していますbc

pythonファイルの内容はprint 6**6**6です。を実行するtime python test.pyと、出力が次のようになります

real        0m0.067s
user        0m0.050s
sys         0m0.011s

そして、time echo 6^6^6 | bc私は次の出力を与えたコマンドを実行しました

real        0m0.205s
user        0m0.197s
sys         0m0.005s

これらの結果から、pythonとbcでかかったsys時間はそれぞれ11ミリ秒と5ミリ秒であったことが明らかです。bcコマンドは、SYS時間レベルでのpythonを上回ったが、それがに来るとき、ユーザーとリアルタイムのpythonのほぼ4倍高速紀元前よりでした。そこに行ったかもしれないもの。私はプロセス自体に優先順位を与えていません。この状況を理解しようとしています。


sysコンポーネントはロードにかかる時間のみを与え、ランタイムは出力のユーザーコンポーネントで与えられるということですか?
ガネス14

本当にわからないので、コメントを投稿しました。それは単なる推測です。
テルドン

7
echo | bcパイプのためにサブシェルを起動する必要があります-それはおそらくあなたの余分なユーザー時間の一部が由来した場所です。これを公平なテストにするために、Pythonスクリプトはstdinから読み取る必要がありtime echo 6**6**6 | whatever.pyます。
goldilocks 14

1
むしろbeコマンドラインをスクリプトに入れて、その実行時間を計りたいです。またはを使用しますecho 6^6^6 | time bc
ダニエルクルマン14

1
サイドノート:Pythonでは、6**6**6式は実際にコンパイル時に計算されます。ただし、モジュールからインポートするのではなく、ファイルを直接起動するため、これは問題ではありません。差プットを参照してください10**12345678a.pyファイルやインタラクティブなインタプリタからそれをインポートしてみてください。次に、インタプリタを閉じて再起動し、a再度インポートします。それはロード二度目ながら(Pythonのモジュールをコンパイルされているため)、それは時間の顕著な量を取るべき最初の時間、.pyc瞬間的でなければならない、
Bakuriu

回答:


25

Pythonは、起動時に多数のファイルをインポートします。

% python -c 'import sys; print len(sys.modules)'
39

モジュールを定義するには多くの方法があるため、これらのそれぞれは、Pythonファイルを開くためにさらに多くの試行を必要とします。

% python -vv -c 'pass'
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# trying site.so
# trying sitemodule.so
# trying site.py
# trying site.pyc
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/sitemodule.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.py
# /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.pyc matches /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.py
import site # precompiled from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site.pyc
# trying os.so
# trying osmodule.so
# trying os.py
# trying os.pyc
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/osmodule.so
# trying /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py
# /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc matches /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py
import os # precompiled from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc
    ...

組み込みのものを除く各「試行」には、OSレベル/システムコールが必要であり、各「インポート」は約8つの「試行」メッセージをトリガーするようです。(zipimportを使用してこれを減らす方法があり、PYTHONPATHの各パスには別の呼び出しが必要になる場合があります。)

これは、Pythonが私のマシンで起動する前に、ほぼ200のstatシステムコールがあり、ユーザープログラムがシステムで処理を待機しているため、「time」はそれを「user」ではなく「sys」に割り当てます。

比較すると、テルドンが言ったように、「bc」にはそれほど高い起動コストはありません。dtrussの出力(Macがあります; LinuxベースのOSの場合は「strace」)を見ると、bcは、いくつかの共有をロードすることを除いて、独自のopen()またはstat()システムコールを行いません。ライブラリが出発点であり、もちろんPythonも同様です。さらに、Pythonは何かを処理する準備が整う前に、読み込むファイルが増えています。

ディスクの待機が遅い。

Pythonの起動コストを理解するには、次を実行します。

time python -c pass

私のマシンでは0.032秒ですが、「print 6 ** 6 ** 6」は0.072秒なので、起動コストは全体の時間の1/2であり、計算+ 10進数への変換は残りの半分です。一方:

time echo 1 | bc

0.005sを要し、「6 ^ 6 ^ 6」は0.184sを要します。そのため、bcの累乗はPythonの累乗よりも4倍以上遅くなります。


4
あなたはちょっとそこに鉛を埋めました。終了ビットを最上部に移動することもできます。
ライキング14

私のマシンでは興味がありません:time python -c 'pass' 0m0.025s、time python -c 'print 6 6 6' 0m0.087s but time python -c 'x = 6 6 6' 0m0.028sだからほとんどの時間は大きな数を出力しています。
スティーブバーンズ14

はい、基数10への変換には桁数で2次時間がかかります。極端な場合として、より大きなメルセンヌ素数の1つを印刷してみてください。これは、計算することが非常に高速ですが、ベース10には、印刷に時間がかかります
アンドリューDalke

11

私はさまざまなフィールドを説明するSOでいい答えを見つけました:

  • 実質は壁時計時間です-コールの開始から終了までの時間。これは、他のプロセスが使用するタイムスライスやプロセスがブロックされるのに費やした時間(I / Oの完了を待機している場合など)を含むすべての経過時間です。

  • ユーザーは、プロセス内の(カーネル外の)ユーザーモードコードで費やされたCPU時間です。これは、プロセスの実行に使用される実際のCPU時間のみです。他のプロセスと、プロセスがブロックするのに費やした時間は、この数値にはカウントされません。

  • Sysは、プロセス内のカーネルで費やされたCPU時間です。これは、ユーザー空間で実行されているライブラリコードとは対照的に、カーネル内のシステムコールに費やされたCPU時間を実行することを意味します。「ユーザー」と同様、これはプロセスが使用するCPU時間のみです。カーネルモード(「スーパーバイザー」モードとも呼ばれる)とシステムコールメカニズムの簡単な説明については、以下を参照してください。

したがって、特定の例では、Pythonバージョンは、完了するのにかかる実際の時間の点でより高速です。ただし、Pythonのアプローチはカーネル空間でより多くの時間を費やし、カーネル関数を呼び出します。このbcコマンドは本質的にカーネル空間で時間を費やすことはなく、ユーザー空間で時間を費やし、おそらく内部bcコードを実行します。

これはあなたには何の違いもありません、あなたが本当に気にする唯一の情報はreal、コマンドを起動してから出力を取得するまでに実際に経過した時間です。

また、これらの小さな違いは安定しておらず、システムの負荷にも依存し、コマンドを実行するたびに変わることに注意する必要があります。

$ for i in {1..10}; do ( time python test.py > /dev/null ) 2>&1; done | grep user
user    0m0.056s
user    0m0.052s
user    0m0.052s
user    0m0.052s
user    0m0.060s
user    0m0.052s
user    0m0.052s
user    0m0.056s
user    0m0.048s
user    0m0.056s

$ for i in {1..10}; do ( time echo 6^6^6 | bc > /dev/null ) 2>&1; done | grep user
user    0m0.188s
user    0m0.188s
user    0m0.176s
user    0m0.176s
user    0m0.172s
user    0m0.176s
user    0m0.180s
user    0m0.172s
user    0m0.172s
user    0m0.172s

10

別の観点から説明します。

公平を期すためにbc、ディスクから何も読み取る必要がなく、blob /バイナリのみが必要なため、Pythonは一連のモジュールをインポートし、ファイルを読み取るため、利点があります。したがって、テストはに偏ってbcいる可能性があります。実際にテストするにはbc -q file、次をfile含む場所を使用する必要があります。

6^6^6
quit

それだけを変更すると、使用する時間が変更されましたecho

bc  0.33s user 0.00s system 80% cpu 0.414 total

ファイルを使用するには:

bc -q some  0.33s user 0.00s system 86% cpu 0.385 total

(より大きな違いに気付くには、terdonの方法を使用する必要がありますが、少なくともそれらは違います)

今、pythonの観点から、pythonはディスクから読み込み、ファイルを毎回コンパイルして実行する必要があり、さらにAndrew pointsとしてモジュールをロードする必要があるため、実行時間が遅くなります。Pythonスクリプトのバイトコードをコンパイルすると、コードの実行にかかる時間が全体で50%短縮されます。

python some.py > /dev/null  0.25s user 0.01s system 63% cpu 0.413 total

コンパイル済み:

./some.pyc  0.22s user 0.00s system 77% cpu 0.282 total

ご覧のとおり、異なるツール間の時間の実行に影響を与える可能性のある要因がいくつかあります。


3

私は他の答えを読むことができました。私のような初心者の人が知っておくべきのために、ここでこのような巨大な整数を持つ理由は、我々している取引は、両方のことであるPythonとはbcやる右結合、これはされていないことを意味指数の拡張、6^36我々は評価しているのではなく6^46656、かなり大きくしたが。1

次のコマンドのバリエーションを使用して、time予約語とコマンドの両方の出力の特定の要素の平均を抽出できます。

for i in {1..1000}; do (time echo 6^6^6 | bc > /dev/null) 2>&1; done | grep 'rea' | sed -e s/.*m// | awk '{sum += $1} END {print sum / NR}'

for i in {1..1000}; do (/usr/bin/time -v sh -c 'echo 6^6^6 | bc > /dev/null') 2>&1; done | grep 'Use' | sed -e s/.*:// | awk '{sum += $1} END {print sum / NR}'

別のルートに移動して、比較からファイルを完全に削除することができます。また、bcのタイミングをdcコマンドのようなものと比較できます。歴史的に前者は後者の「フロントエンドプロセッサ」であるためです。次のコマンドのタイミングが取られました。

echo 6^6^6 | bc
echo 6 6 6 ^ ^ p | dc
echo print 6**6**6 | python2.7

このdcコマンドは、べき乗のために左結合です。2

time1000回の反復(秒)で(bash)を使用した結果がいくつかあります。

0.229678 real bc
0.228348 user bc
0.000569 sys bc
0.23306  real dc
0.231786 user dc
0.000395 sys dc
0.07 real python
0.065907 user python
0.003141 sys python

bcdcこのコンテキストで同等のパフォーマンスを提供します。

すなわち、GNU コマンドからの精度が低い3つの結果(スケール精度はここでは無効ですが、結果は同様です):/usr/bin/timetime

0.2224 user bc
0 sys bc
0.23 Elapsed bc
0.22998 user dc
0 sys dc
0.23 Elapsed dc
0.06008 user python
0 sys python
0.07 Elapsed python

利点/usr/bin/timeは、-v最終的に役立つ可能性のあるはるかに多くの情報を生成するオプションを提供することです。

Pythonモジュールと話すために、これを内部的に評価することもできtimeitます。

python2.7 -m timeit -n 1000 -r 1 'print 6**6**6' | grep 'loops'
1000 loops, best of 1: 55.4 msec per loop

これは、以前見たものよりも少し高速です。インタープリター自体を試してみましょう。

>>> import timeit
>>> import sys
>>> import os
>>> T = timeit.Timer("print 6**6**6")
>>> n = int(1000)
>>> f = open(os.devnull, 'w')
>>> sys.stdout = f
>>> t = t.timeit(n)
>>> sys.stdout = sys.__stdout__
>>> print t/n
0.0553743481636

それは私が見た中で最も速いです。


のようなより小さいべき乗を評価する場合6^6、timeコマンドは驚くべき結果をもたらします-使用したのと同じforループコマンドを使用すると、次のようになります。

0.001001 bc real
0.000304 user
0.000554 sys
0.014    python real i.e. 10x more than bc??
0.010432 user
0.002606 sys

それで、整数bcが小さいほど、突然、ずっと速くなりますか?? システムの再起動から2回目の実行まで、違いはありません。同時に、timeitPythonに使用する場合、次のようになります。

python2.7 -m timeit -n 100000 -r 1 'print 6**6' | grep loops  
100000 loops, best of 1: 0.468 usec per loop

これはミリではなくマイクロ秒であるため、forループを使用した非常に遅い結果とは一致しません。これをさらにテストするために他のツールが必要な場合もあります。質問のシナリオではPythonの方が速かったようですが、それ以上に結論を引き出すことができるかどうかは明らかではありません...


1.それは、エコーの算術拡大すなわちのようなものの範囲を超えてい言うまでもなくecho $((6**6**6))- bashまた、そのIE用右結合であることを起こります6^6^6 = 6^(6^6)

2.これと比較してください6 6 ^ 6 ^ p

3. BSD UNIX(GNU time info document)で実行すると、GNU timeコマンドがより多くの情報を提供する可能性があります: 'time'で表示されるほとんどの情報は、 'wait3'システムコールから取得されます。数値は、「wait3」によって返される数値と同等です。多くのシステムは、「時間」がレポートできるすべてのリソースを測定するわけではありません。これらのリソースはゼロとして報告されます。ほとんどまたはすべてのリソースを測定するシステムは、4.2または4.3BSDに基づいています。以降のBSDリリースでは、より少ないリソースを測定するさまざまなメモリ管理コードが使用されます。-ステータス情報を返す「wait3」コールを持たないシステムでは、代わりに「times」システムコールが使用されます。提供される情報は「wait3」よりはるかに少ないため、これらのシステムでは「time」はほとんどのリソースをゼロとして報告します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.