ich hätte da ein paar Fragen, welche Möglicherweise die Hilfsbereitschaft dieses Forums etwas zu sehr in Anspruch nehmen.
Sollte dieses der Fall sein, schreibt dieses bitte offen und ehrlich.
Code: Alles auswählen
import time
import itertools
import dis
#import profile
# externe Module
# https://pypi.python.org/pypi/profilehooks
#from profilehooks import profile, coverage, timecall
#@profile
def with_itertools(x, y):
return itertools.product(xrange(x), xrange(y))
#@profile
def with_yield(x, y):
for i in xrange(x):
for j in xrange(y):
yield(i, j)
#@profile
def without_yield(x, y):
return ((i, j) for i in xrange(x) for j in xrange(y))
#@profile
def start():
x = 1000
y = 1000
tsec = time.clock()
out = list(without_yield(x,y))
print 'Runtime function (x={0},y={1}) : {2:.6f} sec.'.format(x, y, time.clock() -tsec)
tsec = time.clock()
out = list(with_itertools(x,y))
print 'Runtime itertools (x={0},y={1}) : {2:.6f} sec.'.format(x, y, time.clock() -tsec)
tsec = time.clock()
out = list(with_yield(x,y))
print 'Runtime yield (x={0},y={1}) : {2:.6f} sec.'.format(x, y, time.clock() -tsec)
#profile.run('start(); print')
start()
print '-' *25, 'without_yield' , '-' *25
dis.dis(without_yield.func_code.co_consts[1])
print '-' *25, 'with_yield' , '-' *25
dis.dis(with_yield)
print '-' *25, 'with_itertools' , '-' *25
dis.dis(with_itertools)
Code: Alles auswählen
Runtime function (x=1000,y=1000) : 0.279896 sec.
Runtime itertools (x=1000,y=1000) : 0.236338 sec.
Runtime yield (x=1000,y=1000) : 0.244184 sec.
------------------------- without_yield -------------------------
22 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 36 (to 42)
6 STORE_FAST 1 (i)
9 LOAD_GLOBAL 0 (xrange)
12 LOAD_DEREF 0 (y)
15 CALL_FUNCTION 1
18 GET_ITER
>> 19 FOR_ITER 17 (to 39)
22 STORE_FAST 2 (j)
25 LOAD_FAST 1 (i)
28 LOAD_FAST 2 (j)
31 BUILD_TUPLE 2
34 YIELD_VALUE
35 POP_TOP
36 JUMP_ABSOLUTE 19
>> 39 JUMP_ABSOLUTE 3
>> 42 LOAD_CONST 0 (None)
45 RETURN_VALUE
------------------------- with_yield -------------------------
16 0 SETUP_LOOP 54 (to 57)
3 LOAD_GLOBAL 0 (xrange)
6 LOAD_FAST 0 (x)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 40 (to 56)
16 STORE_FAST 2 (i)
17 19 SETUP_LOOP 31 (to 53)
22 LOAD_GLOBAL 0 (xrange)
25 LOAD_FAST 1 (y)
28 CALL_FUNCTION 1
31 GET_ITER
>> 32 FOR_ITER 17 (to 52)
35 STORE_FAST 3 (j)
18 38 LOAD_FAST 2 (i)
41 LOAD_FAST 3 (j)
44 BUILD_TUPLE 2
47 YIELD_VALUE
48 POP_TOP
49 JUMP_ABSOLUTE 32
>> 52 POP_BLOCK
>> 53 JUMP_ABSOLUTE 13
>> 56 POP_BLOCK
>> 57 LOAD_CONST 0 (None)
60 RETURN_VALUE
------------------------- with_itertools -------------------------
12 0 LOAD_GLOBAL 0 (itertools)
3 LOAD_ATTR 1 (product)
6 LOAD_GLOBAL 2 (xrange)
9 LOAD_FAST 0 (x)
12 CALL_FUNCTION 1
15 LOAD_GLOBAL 2 (xrange)
18 LOAD_FAST 1 (y)
21 CALL_FUNCTION 1
24 CALL_FUNCTION 2
27 RETURN_VALUE
In der Funktion without_yield() kann ich erkennen, dass y mit LOAD_DEREF und im Generator with_yield() und in der Funktion with_itertools() y mit LOAD_FAST verarbeitet wird.
Nach meinen Recherchen ist LOAD_FAST wesentlich schneller als LOAD_DEREF in der Verarbeitung.
LOAD_FAST, LOAD_DEREF, etc. bezeichne ich im weiterem Text als code objecte.
Meine Fragen hierzu wären:
Wo steht in der Dokumentation, welche code objecte ein günstiges und welche eher ein kritisches Laufzeitverhalten haben ?
Wenn derartige Informationen nicht in der Dokumentation stehen, wo kann ich weitergehende Informationen finden ?
Welche code objecte sollten eine besondere Beachtung finden ?
Wie viele verschiedene code objecte gibt es ?
Wenn ich dieses Antworten nur im Sourcecode der GIL finden kann, wo fange ich da an zu suchen ?
Wenn ich mir die einzelnen Ergebnisse des Disassemblers anschaue, müsste unter allen Bedingungen die funktion with_itertools() die performanteste Lösung sein.
Quod esset demonstrandum.
2. Laufzeitverhalten
Code: Alles auswählen
# Ich habe die jeweilige Code-Kombination 10x durchlaufen lassen, dann das jeweils beste und schlechteste Ergebnis gestrichen und
# aus den verbliebenen 8 Werten jeweils einen Mittelwert errechnet des jeweiligen Programm print out ermittelt:
Runtime yield (x=5,y=5) : 0.000012 sec.
Runtime function (x=5,y=5) : 0.000027 sec.
Runtime itertools (x=5,y=5) : 0.000025 sec.
Runtime itertools (x=5,y=5) : 0.000013 sec.
Runtime function (x=5,y=5) : 0.000051 sec.
Runtime yield (x=5,y=5) : 0.000083 sec.
Runtime function (x=5,y=5) : 0.000014 sec.
Runtime itertools (x=5,y=5) : 0.000080 sec.
Runtime yield (x=5,y=5) : 0.000102 sec.
Runtime yield (x=1000,y=1000) : 0.200122 sec.
Runtime function (x=1000,y=1000) : 0.245055 sec.
Runtime itertools (x=1000,y=1000) : 0.188157 sec.
Runtime itertools (x=1000,y=1000) : 0.139838 sec.
Runtime function (x=1000,y=1000) : 0.240710 sec.
Runtime yield (x=1000,y=1000) : 0.251581 sec.
Runtime function (x=1000,y=1000) : 0.198934 sec.
Runtime itertools (x=1000,y=1000) : 0.186137 sec.
Runtime yield (x=1000,y=1000) : 0.233364 sec.
Code: Alles auswählen
# Ergebnis des Moduls profile aus der Standardbibliothek
# x=1000 und y=1000
2000017 function calls in 8.702 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
6 0.000 0.000 0.000 0.000 :0(clock)
3 0.000 0.000 0.000 0.000 :0(format)
1 0.021 0.021 0.021 0.021 :0(setprofile)
1 0.057 0.057 8.682 8.682 <string>:1(<module>)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 8.702 8.702 profile:0(start(); print)
1000001 2.134 0.000 2.134 0.000 python_forum.de.py:11(with_yield)
1 0.000 0.000 0.000 0.000 python_forum.de.py:16(without_yield)
1000001 2.201 0.000 2.201 0.000 python_forum.de.py:17(<genexpr>)
1 4.289 4.289 8.624 8.624 python_forum.de.py:20(start)
1 0.000 0.000 0.000 0.000 python_forum.de.py:7(with_itertools)
Code: Alles auswählen
# Ergebnis mit kernprof.exe -l -v <modulname.py> - https://github.com/rkern/line_profiler
Runtime function (x=1000,y=1000) : 0.510616 sec.
Runtime itertools (x=1000,y=1000) : 0.193372 sec.
Runtime yield (x=1000,y=1000) : 9.540016 sec.
Wrote profile results to python_forum.de.py.lprof
Timer unit: 3.41328e-07 s
Total time: 0.000105129 s
File: .\python_forum.de.py
Function: with_itertools at line 6
Line # Hits Time Per Hit % Time Line Contents
==============================================================
6 @profile
7 def with_itertools(x, y):
8 1 308 308.0 100.0 return itertools.product(xrange(x), xrange(y))
Total time: 3.03512 s
File: .\python_forum.de.py
Function: with_yield at line 10
Line # Hits Time Per Hit % Time Line Contents
==============================================================
10 @profile
11 def with_yield(x, y):
12 1001 4450 4.4 0.1 for i in xrange(x):
13 1001000 4480051 4.5 50.4 for j in xrange(y):
14 1000000 4407598 4.4 49.6 yield(i, j)
Total time: 3.7546e-06 s
File: .\python_forum.de.py
Function: without_yield at line 16
Line # Hits Time Per Hit % Time Line Contents
==============================================================
16 @profile
17 def without_yield(x, y):
18 1 11 11.0 100.0 return ((i, j) for i in xrange(x) for j in xrange(y))
Total time: 10.2524 s
File: .\python_forum.de.py
Function: start at line 20
Line # Hits Time Per Hit % Time Line Contents
==============================================================
20 @profile
21 def start():
22 1 9 9.0 0.0 x = 1000
23 1 6 6.0 0.0 y = 1000
24
25
26 1 17 17.0 0.0 tsec = time.clock()
27 1 1495912 1495912.0 5.0 out = list(without_yield(x,y))
28 1 5592 5592.0 0.0 print 'Runtime function (x={0},y={1}) : {2:.6f} sec.'.format(x, y, time.clock() -tsec)
29
30 1 31 31.0 0.0 tsec = time.clock()
31 1 566414 566414.0 1.9 out = list(with_itertools(x,y))
32 1 12384 12384.0 0.0 print 'Runtime itertools (x={0},y={1}) : {2:.6f} sec.'.format(x, y, time.clock() -tsec)
33
34 1 61 61.0 0.0 tsec = time.clock()
35 1 27949615 27949615.0 93.1 out = list(with_yield(x,y))
36 1 6917 6917.0 0.0 print 'Runtime yield (x={0},y={1}) : {2:.6f} sec.'.format(x, y, time.clock() -tsec)
Code: Alles auswählen
# Ergebnisse mit aktiviertem
# from profilehooks import profile, coverage, timecall
Runtime function (x=1000,y=1000) : 2.055206 sec.
Runtime itertools (x=1000,y=1000) : 0.195309 sec.
Runtime yield (x=1000,y=1000) : 2.076854 sec.
*** PROFILER RESULTS ***
start (.\python_forum.de.py:24)
function called 1 times
2000021 function calls in 4.332 seconds
Ordered by: cumulative time, internal time, call count
ncalls tottime percall cumtime percall filename:lineno(function)
1 2.235 2.235 4.332 4.332 python_forum.de.py:24(start)
1000001 1.057 0.000 1.057 0.000 python_forum.de.py:22(<genexpr>)
1000001 1.040 0.000 1.040 0.000 python_forum.de.py:14(with_yield)
3 0.000 0.000 0.000 0.000 profilehooks.py:234(new_fn)
3 0.000 0.000 0.000 0.000 profilehooks.py:328(__call__)
3 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects}
1 0.000 0.000 0.000 0.000 python_forum.de.py:10(with_itertools)
6 0.000 0.000 0.000 0.000 {time.clock}
1 0.000 0.000 0.000 0.000 python_forum.de.py:20(without_yield)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
0 0.000 0.000 profile:0(profiler)
*** PROFILER RESULTS ***
without_yield (.\python_forum.de.py:20)
function called 1 times
0 function calls in 0.000 seconds
Ordered by: cumulative time, internal time, call count
ncalls tottime percall cumtime percall filename:lineno(function)
0 0.000 0.000 profile:0(profiler)
*** PROFILER RESULTS ***
with_yield (.\python_forum.de.py:14)
function called 1 times
0 function calls in 0.000 seconds
Ordered by: cumulative time, internal time, call count
ncalls tottime percall cumtime percall filename:lineno(function)
0 0.000 0.000 profile:0(profiler)
*** PROFILER RESULTS ***
with_itertools (.\python_forum.de.py:10)
function called 1 times
0 function calls in 0.000 seconds
Ordered by: cumulative time, internal time, call count
ncalls tottime percall cumtime percall filename:lineno(function)
0 0.000 0.000 profile:0(profiler)
Wie macht man das mit dem Profiling denn nun richtig, sodass die Ergebnisse auch wirklich belastbar sind ?
Welche Module und/oder Tools sind nützlich und von welchen sollte man lieber die Finger lassen ?
Beschäftige ich mich hier eher mit einem akademischen Thema oder ist dies in der Praxis nützlich ?
Zusammenfassend:
Ich benutze das modul dis und überwiegend kernprof. Doch ich zweifel die einzelnen Ergebnisse an.
Wenn ich die Reihenfolge der einzelnen Funktionen in meinem Code zum Beispiel von
a.) 1. list(without_yield(x,y)), 2. list(with_itertools(x,y)) und 3. list(with_yield(x,y)) nach
b.) 1. list(with_yield(x,y)), 2. list(without_yield(x,y)) und 3. list(with_itertools(x,y))
ändere, bekomme ich auch völlig andere Ergebnisse. Das kann eigentlich so nicht sein.