2016-07-09 5 views
0

diesen Leistungstest Betrachten auf IPython unter Python 3:numpy.sum auf Reichweite und range_iterator Objekte

einen Bereich erstellen einen range_iterator und einen Generator

In [1]: g1 = range(1000000) 

In [2]: g2 = iter(range(1000000)) 

In [3]: g3 = (i for i in range(1000000)) 

Measure Zeit für native Summe mit Python Summieren

In [4]: %timeit sum(g1) 
10 loops, best of 3: 47.4 ms per loop 

In [5]: %timeit sum(g2) 
The slowest run took 374430.34 times longer than the fastest. This could mean that an intermediate result is being cached. 
10000000 loops, best of 3: 123 ns per loop 

In [6]: %timeit sum(g3) 
The slowest run took 1302907.54 times longer than the fastest. This could mean that an intermediate result is being cached. 
10000000 loops, best of 3: 128 ns per loop 

Nicht sicher, ob ich über die Warnung sorgen sollte. Das Timing der Bereichsversion ist unterschiedlich lang (warum?), Aber der Range_iterator und der Generator sind ähnlich.

Jetzt ist numpy.sum

In [7]: import numpy as np 

In [8]: %timeit np.sum(g1) 
10 loops, best of 3: 174 ms per loop 

In [9]: %timeit np.sum(g2) 
The slowest run took 8.47 times longer than the fastest. This could mean that an intermediate result is being cached. 
100000 loops, best of 3: 6.51 µs per loop 

In [10]: %timeit np.sum(g3) 
The slowest run took 9.59 times longer than the fastest. This could mean that an intermediate result is being cached. 
1000000 loops, best of 3: 446 ns per loop 

g1 und g3 wurde x ~ 3.5 langsamer, aber die range_iterator g2 ist jetzt einige ~ 50-mal langsamer im Vergleich zur nativen Summe verwenden lassen. g3 gewinnt.

In [11]: type(g1) 
Out[11]: range 

In [12]: type(g2) 
Out[12]: range_iterator 

In [13]: type(g3) 
Out[13]: generator 

Warum eine solche Strafe auf range_iterator auf numpy.sum? Sollten solche Objekte vermieden werden? Verallgemeinert - Generieren "hausgemachte" Generatoren immer andere Objekte auf numpigen?

EDIT 1: Ich erkannte, dass die np.sum den range_iterator nicht auswertet, sondern ein anderes range_iterator Objekt zurückgibt. Dieser Vergleich ist also nicht gut. Warum wird es nicht ausgewertet?

BEARBEITEN 2: Ich erkannte auch, dass numpy.sum den Bereich in ganzzahliger Form hält und dementsprechend die falschen Ergebnisse zu meiner Summe aufgrund Integerüberlauf gibt.

In [12]: sum(range(1000000)) 
Out[12]: 499999500000 

In [13]: np.sum(range(1000000)) 
Out[13]: 1783293664 

In [14]: np.sum(range(1000000), dtype=float) 
Out[14]: 499999500000.0 

Zwischenfazit - nicht numpy.sum auf nicht numpy Objekte verwenden ...?

+0

Dies wiederholt eine vorherige ähnliche Frage, die off-topic markiert wurde. Hoffentlich finden Sie dieses zum Thema. – Aguy

+0

Mögliches Duplikat von [Warum sind Pythons Arrays langsam?] (Http://stackoverflow.com/questions/36778568/why-are-pythons-arrays-slow) – styvane

+1

@SSDMS - diese Antwort ist für 'import array', nicht' import numpy'. – hpaulj

Antwort

3

Haben Sie sich die Ergebnisse von wiederholten Summen auf der Iter angesehen?

95:~/mypy$ g2=iter(range(10)) 
96:~/mypy$ sum(g2) 
Out[96]: 45 
97:~/mypy$ sum(g2) 
Out[97]: 0 
98:~/mypy$ sum(g2) 
Out[98]: 0 

Warum die 0? Denn g2 kann nur einmal verwendet werden. Das gleiche gilt für den Generatorausdruck.

Oder schauen Sie es mit der Liste

100:~/mypy$ g2=iter(range(10)) 
101:~/mypy$ list(g2) 
Out[101]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
102:~/mypy$ list(g2) 
Out[102]: [] 

In Python 3 range ist ein Range-Objekt, keine Liste. Es ist also ein Iterator, der bei jeder Verwendung neu generiert wird.

Wie für np.sum, muss np.sum(range(10)) ein Array zuerst machen.

Wenn auf einer Liste arbeitet, wird der Python sum ist recht schnell, schneller als np.sum auf dem gleichen:

116:~/mypy$ %%timeit x=list(range(10000)) 
     ...: sum(x) 
1000 loops, best of 3: 202 µs per loop 

117:~/mypy$ %%timeit x=list(range(10000)) 
     ...: np.sum(x) 
1000 loops, best of 3: 1.62 ms per loop 

Aber Betrieb auf einem Array, np.sum tut viel besser

118:~/mypy$ %%timeit x=np.arange(10000) 
     ...: sum(x) 
100 loops, best of 3: 5.92 ms per loop 

119:~/mypy$ %%timeit x=np.arange(10000) 
     ...: np.sum(x) 
<caching warning> 
100000 loops, best of 3: 18.6 µs per loop 

Ein anderes Timing - verschiedene Möglichkeiten, ein Array zu erstellen.fromiter kann schneller sein als np.array; aber das eingebaute arange ist viel besser.

124:~/mypy$ timeit np.array(range(100000)) 
10 loops, best of 3: 39.2 ms per loop 
125:~/mypy$ timeit np.fromiter(range(100000),int) 
100 loops, best of 3: 12.9 ms per loop 
126:~/mypy$ timeit np.arange(100000) 
The slowest run took 6.93 times longer than the fastest. This could mean that an intermediate result is being cached. 
10000 loops, best of 3: 106 µs per loop 

Verwenden range, wenn Sie mit Listen arbeiten wollen; aber verwenden Sie numpy's eigenen Bereich, wenn Sie mit Arrays arbeiten müssen. Beim Erstellen von Arrays entsteht ein Overhead, so dass sie bei der Arbeit mit großen Arrays wertvoller sind.

==================

Auf die Frage, wie np.sum einen Iterator Griffe - es funktioniert nicht. Schauen Sie, was np.array tut, um ein solches Objekt:

In [12]: np.array(iter(range(10))) 
Out[12]: array(<range_iterator object at 0xb5998f98>, dtype=object) 

Es erzeugt ein einzelnes Element Array mit dtype Objekt.

fromiter wird diese iterable bewerten:

In [13]: np.fromiter(iter(range(10)),int) 
Out[13]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

np.array folgt einige komplizierte Regeln, wenn es um die Umwandlung der Eingabe in ein Array kommt. Es wurde entwickelt, um hauptsächlich mit einer Liste von Zahlen oder verschachtelten Listen gleicher Länge zu arbeiten.

Wenn Sie Fragen dazu haben, wie eine np-Funktion ein Nicht-Array-Objekt behandelt, überprüfen Sie zuerst, was np.array mit diesem Objekt macht.

Verwandte Themen