0

私が理解しようとしている奇妙な結果が得られました。約 325k 行 (リスト) のデータセットがあり、それぞれ約 90 個のアイテム (文字列、フロートなど - それは実際には問題ではありません) があります。たとえば、すべてのアイテムに対して何らかの処理を行いたい場合は、2 つの「for」を使用してそれらを反復処理できます。

for eachRow in rows:
    for eachItem in eachRow:
        # do something

私のシステムでは、このコードは 41 秒間実行されました。しかし、ネストされたループを一連のインデックス アクセス ( eachRow[0]、eachRowm[1] から eachRow[89] まで) に置き換えると、実行時間は 25 秒に短縮されます。

for eachRow in rows:
    eachRow[0]  # do something with this item
    eachRow[1]  # do something with this item
    ..
    eachRow[89] # do something with this item

もちろん、そのようなコードを書くのは良い考えではありません。データ処理のパフォーマンスを向上させる方法を探していたところ、偶然この奇妙なアプローチを見つけました。コメントはありますか?

4

3 に答える 3

1

アンローリングを行うことでわずかにパフォーマンスが向上するように見えますが、無視できる程度であり、do_something関数が実際にほとんど何もしない場合を除き、違いはわかりません。異なるアプローチで同等の動作が 60% になるとは信じがたいですが、私が考えもしなかったいくつかの実装の詳細に常に驚かされたいと思っています。

tl;dr 要約、せっかちなので 325000 の代わりに 32500 を使用:

do_nothing easy 3.44702410698
do_nothing indexed 3.99766016006
do_nothing mapped 4.36127090454
do_nothing unrolled 3.33416581154
do_something easy 5.4152610302
do_something indexed 5.95649385452
do_something mapped 6.20316290855
do_something unrolled 5.2877831459
do_more easy 16.6573209763
do_more indexed 16.8381450176
do_more mapped 17.6184959412
do_more unrolled 16.0713188648

CPython 2.7.3、コード:

from timeit import Timer

nrows = 32500
ncols = 90
a = [[1.0*i for i in range(ncols)] for j in range(nrows)]

def do_nothing(x):
    pass

def do_something(x):
    z = x+3
    return z

def do_more(x):
    z = x**3+x**0.5+4
    return z

def easy(rows, action):
    for eachRow in rows:
        for eachItem in eachRow:
            action(eachItem)

def mapped(rows, action):
    for eachRow in rows:
        map(action, eachRow)

def indexed(rows, action):
    for eachRow in rows:
        for i in xrange(len(eachRow)):
            action(eachRow[i])

def unrolled(rows, action):
    for eachRow in rows:
        action(eachRow[0])
        action(eachRow[1])
        action(eachRow[2])
        action(eachRow[3])
        action(eachRow[4])
        action(eachRow[5])
        action(eachRow[6])
        action(eachRow[7])
        action(eachRow[8])
        action(eachRow[9])
        action(eachRow[10])
        action(eachRow[11])
        action(eachRow[12])
        action(eachRow[13])
        action(eachRow[14])
        action(eachRow[15])
        action(eachRow[16])
        action(eachRow[17])
        action(eachRow[18])
        action(eachRow[19])
        action(eachRow[20])
        action(eachRow[21])
        action(eachRow[22])
        action(eachRow[23])
        action(eachRow[24])
        action(eachRow[25])
        action(eachRow[26])
        action(eachRow[27])
        action(eachRow[28])
        action(eachRow[29])
        action(eachRow[30])
        action(eachRow[31])
        action(eachRow[32])
        action(eachRow[33])
        action(eachRow[34])
        action(eachRow[35])
        action(eachRow[36])
        action(eachRow[37])
        action(eachRow[38])
        action(eachRow[39])
        action(eachRow[40])
        action(eachRow[41])
        action(eachRow[42])
        action(eachRow[43])
        action(eachRow[44])
        action(eachRow[45])
        action(eachRow[46])
        action(eachRow[47])
        action(eachRow[48])
        action(eachRow[49])
        action(eachRow[50])
        action(eachRow[51])
        action(eachRow[52])
        action(eachRow[53])
        action(eachRow[54])
        action(eachRow[55])
        action(eachRow[56])
        action(eachRow[57])
        action(eachRow[58])
        action(eachRow[59])
        action(eachRow[60])
        action(eachRow[61])
        action(eachRow[62])
        action(eachRow[63])
        action(eachRow[64])
        action(eachRow[65])
        action(eachRow[66])
        action(eachRow[67])
        action(eachRow[68])
        action(eachRow[69])
        action(eachRow[70])
        action(eachRow[71])
        action(eachRow[72])
        action(eachRow[73])
        action(eachRow[74])
        action(eachRow[75])
        action(eachRow[76])
        action(eachRow[77])
        action(eachRow[78])
        action(eachRow[79])
        action(eachRow[80])
        action(eachRow[81])
        action(eachRow[82])
        action(eachRow[83])
        action(eachRow[84])
        action(eachRow[85])
        action(eachRow[86])
        action(eachRow[87])
        action(eachRow[88])
        action(eachRow[89])


def timestuff():
    for action in 'do_nothing do_something do_more'.split():
        for name in 'easy indexed mapped unrolled'.split():
            t = Timer(setup="""
from __main__ import {} as fn
from __main__ import {} as action
from __main__ import a
""".format(name, action),
                      stmt="fn(a, action)").timeit(10)
            print action, name, t

if __name__ == '__main__':
    timestuff()

(比較を正確に公平にすることは気にしなかったことに注意してください。なぜなら、変動の可能性のある規模、つまり順序​​の統一性が変化するかどうかを測定しようとしていただけだからです。)

于 2012-09-10T16:53:24.800 に答える
0

申し訳ありませんが、それは私のせいでした。私のシステムに何か問題がありました(これはスタンドアロンのPythonインタープリターではなく、大きなシステムに組み込まれています)。システム全体を再起動した後、正しい結果が得られました。両方のバリアントで約2.8秒です。私は愚かだと感じます。無関係のために私の質問を削除する方法を探しています。

于 2012-09-10T16:51:46.813 に答える
0

これを計った他のレスポンダーとは異なり、私はタイミングにかなり大きな違いを見ました. まず、私のコード:

import random
import string
import timeit

r = 1000
outer1 = [[[''.join([random.choice(string.ascii_letters) for j in range(10)])] for k in range(90)] for l in range(r)]
outer2 = [[[''.join([random.choice(string.ascii_letters) for j in range(10)])] for k in range(90)] for l in range(r)]
outer3 = [[[''.join([random.choice(string.ascii_letters) for j in range(10)])] for k in range(90)] for l in range(r)]

def x1(L):
    for outer in L:
        for inner in L:
            inner = inner[:-1]

def x2(L):
    for outer in L:
        for y in range(len(outer)):
            outer[y] = outer[y][:-1]

def x3(L):
    for x in range(len(L)):
        for y in range(len(L[x])):
            L[x][y] = L[x][y][:-1]

print "x1 =",timeit.Timer('x1(outer1)', "from __main__ import x1,outer1").timeit(10)
print "x2 =",timeit.Timer('x2(outer2)', "from __main__ import x2,outer2").timeit(10)
print "x3 =",timeit.Timer('x3(outer3)', "from __main__ import x3,outer3").timeit(10)

これらをそれぞれ 10 回実行していることに注意してください。各リストには、10 文字のランダムな文字列である 90 個の項目を含む 3000 個の項目が入力されています。

代表的な結果:

x1 = 8.0179214353
x2 = 0.118051644801
x3 = 0.150409681521

インデックスを使用しない関数 (x1) は、内側のループにのみインデックスを使用する関数 (x2) よりも実行に66 倍の時間がかかります。奇妙なことに、内側のループにのみインデックスを使用する関数 (x2) は、外側のループと内側のループの両方にインデックスを使用する関数 (x3) よりも優れたパフォーマンスを発揮します。

于 2012-09-10T17:58:05.673 に答える