Python eingebaute Summenfunktion vs. für Schleifenleistung

Ich bemerkte, dass Pythons eingebaute sum etwa 3x schneller ist als eine for-Schleife beim Summieren einer Liste von 1 000 000 Integern:

 import timeit def sum1(): s = 0 for i in range(1000000): s += i return s def sum2(): return sum(range(1000000)) print 'For Loop Sum:', timeit.timeit(sum1, number=10) print 'Built-in Sum:', timeit.timeit(sum2, number=10) # Prints: # For Loop Sum: 0.751425027847 # Built-in Sum: 0.266746997833 

Warum das? Wie wird die sum umgesetzt?

3 Solutions collect form web for “Python eingebaute Summenfunktion vs. für Schleifenleistung”

Die Geschwindigkeitsdifferenz ist tatsächlich größer als 3 mal, aber Sie verlangsamen entweder Version, indem sie zuerst eine riesige in-Gedächtnisliste von 1 Million Integers verursachen. Trennen Sie das aus den Zeitversuchen:

 >>> import timeit >>> def sum1(lst): ... s = 0 ... for i in lst: ... s += i ... return s ... >>> def sum2(lst): ... return sum(lst) ... >>> values = range(1000000) >>> timeit.timeit('f(lst)', 'from __main__ import sum1 as f, values as lst', number=100) 3.457869052886963 >>> timeit.timeit('f(lst)', 'from __main__ import sum2 as f, values as lst', number=100) 0.6696369647979736 

Die Geschwindigkeitsdifferenz ist jetzt auf über 5 Mal gestiegen.

Eine for Schleife wird als interpretierter Python-Bytecode ausgeführt. sum() Schleifen ganz im C-Code. Die Geschwindigkeitsdifferenz zwischen interpretiertem Bytecode und C-Code ist groß.

Darüber hinaus stellt der C-Code sicher, dass keine neuen Python-Objekte erstellt werden sollen, wenn er die Summe in C-Typen stattdessen beibehalten kann. Dies funktioniert für int und float Ergebnisse.

Die Python-Version, zerlegt, tut dies:

 >>> import dis >>> def sum1(): ... s = 0 ... for i in range(1000000): ... s += i ... return s ... >>> dis.dis(sum1) 2 0 LOAD_CONST 1 (0) 3 STORE_FAST 0 (s) 3 6 SETUP_LOOP 30 (to 39) 9 LOAD_GLOBAL 0 (range) 12 LOAD_CONST 2 (1000000) 15 CALL_FUNCTION 1 18 GET_ITER >> 19 FOR_ITER 16 (to 38) 22 STORE_FAST 1 (i) 4 25 LOAD_FAST 0 (s) 28 LOAD_FAST 1 (i) 31 INPLACE_ADD 32 STORE_FAST 0 (s) 35 JUMP_ABSOLUTE 19 >> 38 POP_BLOCK 5 >> 39 LOAD_FAST 0 (s) 42 RETURN_VALUE 

Abgesehen davon, dass die Interpreter-Schleife langsamer als C ist, wird die INPLACE_ADD ein neues Integer-Objekt erstellen (nach 255, CPython-Caches kleine int Objekte als Singletons).

Sie können die C-Implementierung im Python-Quecksilber-Code-Repository sehen, aber es gibt explizit in den Kommentaren:

 /* Fast addition by keeping temporary sums in C instead of new Python objects. Assumes all inputs are the same type. If the assumption fails, default to the more general routine. */ 

Wie dwanderson vorgeschlagen hat, ist Numpy eine Alternative. Es ist ja, wenn du etwas Mathe machen willst. Siehe diese Benchmark:

 import numpy as np r = range(1000000) # 12.5 ms s = sum(r) # 7.9 ms ar = np.arange(1000000) # 0.5 ms as = np.sum(ar) # 0.6 ms 

Also beide schaffen die Liste und summiert es ist viel schneller mit numpy . Dies ist vor allem, weil die numpy.array ist für diese konzipiert und ist viel effizienter als die Liste.

Allerdings, wenn wir eine Python-Liste haben, dann numpy ist sehr langsam, wie seine Umwandlung von einer Liste in eine numpy.array ist träge:

 r = range(1000000) ar = np.array(r) # 102 ms 

Sie können den Quellcode in Python/bltinmodule.c . Es hat spezielle Fälle für int s und float s, aber da die Summe überläuft zu long s ziemlich schnell, das hat wahrscheinlich nicht eine große Leistung Auswirkungen hier. Die General-Case-Logik ist ziemlich ähnlich, was man in Python schreiben würde, nur in C. Die Beschleunigung ist höchstwahrscheinlich aufgrund der Tatsache, dass es nicht durch alle Bytecode-Interpretation und Fehlerbehandlung Overhead gehen muss:

 static PyObject* builtin_sum(PyObject *self, PyObject *args) { PyObject *seq; PyObject *result = NULL; PyObject *temp, *item, *iter; if (!PyArg_UnpackTuple(args, "sum", 1, 2, &seq, &result)) return NULL; iter = PyObject_GetIter(seq); if (iter == NULL) return NULL; if (result == NULL) { result = PyInt_FromLong(0); if (result == NULL) { Py_DECREF(iter); return NULL; } } else { /* reject string values for 'start' parameter */ if (PyObject_TypeCheck(result, &PyBaseString_Type)) { PyErr_SetString(PyExc_TypeError, "sum() can't sum strings [use ''.join(seq) instead]"); Py_DECREF(iter); return NULL; } Py_INCREF(result); } #ifndef SLOW_SUM /* Fast addition by keeping temporary sums in C instead of new Python objects. Assumes all inputs are the same type. If the assumption fails, default to the more general routine. */ if (PyInt_CheckExact(result)) { long i_result = PyInt_AS_LONG(result); Py_DECREF(result); result = NULL; while(result == NULL) { item = PyIter_Next(iter); if (item == NULL) { Py_DECREF(iter); if (PyErr_Occurred()) return NULL; return PyInt_FromLong(i_result); } if (PyInt_CheckExact(item)) { long b = PyInt_AS_LONG(item); long x = i_result + b; if ((x^i_result) >= 0 || (x^b) >= 0) { i_result = x; Py_DECREF(item); continue; } } /* Either overflowed or is not an int. Restore real objects and process normally */ result = PyInt_FromLong(i_result); temp = PyNumber_Add(result, item); Py_DECREF(result); Py_DECREF(item); result = temp; if (result == NULL) { Py_DECREF(iter); return NULL; } } } if (PyFloat_CheckExact(result)) { double f_result = PyFloat_AS_DOUBLE(result); Py_DECREF(result); result = NULL; while(result == NULL) { item = PyIter_Next(iter); if (item == NULL) { Py_DECREF(iter); if (PyErr_Occurred()) return NULL; return PyFloat_FromDouble(f_result); } if (PyFloat_CheckExact(item)) { PyFPE_START_PROTECT("add", Py_DECREF(item); Py_DECREF(iter); return 0) f_result += PyFloat_AS_DOUBLE(item); PyFPE_END_PROTECT(f_result) Py_DECREF(item); continue; } if (PyInt_CheckExact(item)) { PyFPE_START_PROTECT("add", Py_DECREF(item); Py_DECREF(iter); return 0) f_result += (double)PyInt_AS_LONG(item); PyFPE_END_PROTECT(f_result) Py_DECREF(item); continue; } result = PyFloat_FromDouble(f_result); temp = PyNumber_Add(result, item); Py_DECREF(result); Py_DECREF(item); result = temp; if (result == NULL) { Py_DECREF(iter); return NULL; } } } #endif for(;;) { item = PyIter_Next(iter); if (item == NULL) { /* error, or end-of-sequence */ if (PyErr_Occurred()) { Py_DECREF(result); result = NULL; } break; } /* It's tempting to use PyNumber_InPlaceAdd instead of PyNumber_Add here, to avoid quadratic running time when doing 'sum(list_of_lists, [])'. However, this would produce a change in behaviour: a snippet like empty = [] sum([[x] for x in range(10)], empty) would change the value of empty. */ temp = PyNumber_Add(result, item); Py_DECREF(result); Py_DECREF(item); result = temp; if (result == NULL) break; } Py_DECREF(iter); return result; } 
  • Weird numpy.sum Verhalten beim Hinzufügen von Nullen
  • Runde eine Python-Liste der Zahlen und pflegen die Summe
  • Generiere alle möglichen Listen der Länge N, die zu S in Python summieren
  • Python-Code für Summe mit Bedingung
  • Pandas, Gruppen und Summierung über bestimmte Monate
  • Summenprobe TypError: nicht unterstützter Operanden Typ (s) für +: 'int' und 'str'
  • Python-Datenrahmen: kumulative Summe der Spalte, bis die Bedingung erreicht ist und den Index zurückgibt
  • Wie fasst man das Wort für jede Person in einem Dialog zusammen?
  • Wie man die Gesamtzahl der Zeilen in einer Textdatei mit Python zählt
  • Python - rekursive Summenliste
  • Summierende Fakultäten in Python
  • Python ist die beste Programmiersprache der Welt.