Nuitka vs. PyPy vs. Cython+gcc vs. CPython - Geschwindigkeit

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

habe mal ein wenige mit Nuitka rumgespielt. Und mich über die nicht vorhandene Geschwindigkeit (bei meinem Test) gewundert.

Der Testcode ist ein Test auf Primzahlen aus der Python Doku zu concurrent.futures (Link):

Code: Alles auswählen

import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419,
    777777722155555333,
    777777722155555335,
    9999999900000001,
    2327074306453592351,
    2327074306453592353]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    for number in PRIMES:
        prime = is_prime(number)
        print('{} is prime: {}'.format(number, prime))

if __name__ == '__main__':
    main()
Geschwindigkeiten auf meinen System (Ubuntu 16.04, ruhender Desktop, Intel® Core i5-5200U CPU @ 2.20GHz × 4 ):
CPython 3.5: 3 min 23 s
CPython 3.5 + concurrent.futures.ProcessPoolExecutor: 2 min 15 s
PyPy3.5 v5.10.0: 0 min 24 s
Cython+gcc: 2 min 39 s
Nuitka: 3 min 7 s

PyPy ist als drastisch schneller als alles andere. Nur ist Nuitka ja kaum schneller als CPython. Hat dafür jemand eine Erklärung? Ist mein (zugegebener Maßen simpler) Test "schlecht"? Wobei das andere kompilierte Programm mit Cython+gcc ja eine ganze Ecke schneller ist (aber immer noch drastisch langsamer als Nuitka). Übersetzt habe ich mit `nuitka --standalone prime_code_linear.py`.

Gruß, noisefloor
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@noisefloor: ich glaube »Nuitka« ist noch ein einem so frühen Stadium, dass es noch nicht wirklich optimiert. Bei mir beschleunigt »numba« von 4min 16s auf 0min 16s.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nuitka wirkt wie eine Verschmelzung aus Py2Exe (und ähnliche Tools) mit PyPy, nur dass anscheinend das Rad für beide Bereiche neu erfunden wird. Was soll daran interessant sein? Oder übersehe ich was?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Nuitka ist momentan effektiv nichts anderes als CPython ohne den Interpreter Loop der durch den Bytecode geht und die korrespondierenden Funktionen aufruft. Nuitka ist nur schneller weil es etwas Overhead eliminiert, nicht weil es irgendwelche nennenswerten Optimierungen macht.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ok, Danke für die Antworten.

@Sirius3: kannst du mal posten, wie du das mit Numba gemacht hast bzw wie du den Code für Numba modifiziert hast?

Gruß, noisefloor
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

so, habe mir doch mal Numba via Miniconda installiert. Im oben gezeigten Code habe ich nur `from numba import jit` hinzugefügt und nur die `is_prime` Funktion mit `@jit` dekoriert.

Laufzeit: 0 min 19 s

Ist also am schnellsten.

Hat noch jemand eine Idee, warum die nach C kompilierte Version mit Cython + gcc auch relativ langsam ist?

Gruß, noisefloor
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

noisefloor hat geschrieben: Hat noch jemand eine Idee, warum die nach C kompilierte Version mit Cython + gcc auch relativ langsam ist?
Hast du deinen Code oben unverändert mit Cython in eine Library verwandelt, oder zusätzlich statische Typen angegeben? Mit Typ-Deklarationen (für i, n und is_prime) ist es bei mir ein paar Sekunden schneller als pypy.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@nezzcarth: nee, 1:1 übernommen, als `cython3 --embed meinprogramm.py`

Wie hast du den Code denn geändert, damit statische Type genutzt werden?

Gruß, noisefloor
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

@noisefloor:
Ich habe mit Cython nur sehr wenig Erfahrung und weiß daher nicht, ob das der richtige Weg ist. Folgendes habe ich gemacht:

[codebox=python file=primes.pyx]
import math

cdef bint is_prime(long n):
cdef int i
if n % 2 == 0:
return False

sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True


def test_primes():
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419,
777777722155555333,
777777722155555335,
9999999900000001,
2327074306453592351,
2327074306453592353]
for number in PRIMES:
prime = is_prime(number)
print('{} is prime: {}'.format(number, prime))
[/code]

Dies habe ich übersetzt mit 'cythonize -i primes.pyx' und die resultierende Library in ein Wrapper-Script importiert, das einfach nur 'test_primes' aufruft. Die Laufzeit beträgt so um die 13.5 Sekunden. Pypy braucht mit deinem Original-Skript c.a. 18 Sekunden.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ah, ok. Hab's mal so ähnlich gemacht:

Code: Alles auswählen

import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419,
    777777722155555333,
    777777722155555335,
    9999999900000001,
    2327074306453592351,
    2327074306453592353]

cdef bint is_prime(long n):
    cdef int sqrt_n, i
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    cdef long number
    cdef bint prime 
    for number in PRIMES:
        prime = is_prime(number)
        print('{} is prime: {}'.format(number, prime))

if __name__ == '__main__':
    main()
Laufzeit: 0 min 18 s

Also die kürzeste Laufzeit. Wobei sich zumindest in diesem Fall der Mehraufwand gegenüber PyPy IMHO nicht wirklich lohnt.

Gruß, noisefloor
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Der Cython Code funktioniert dann auch nur noch mit longs, tut also auch nicht dass gleiche. Außerdem muss bei PyPy der JIT erstmal warm werden, dies misst du hier mit spielt aber in der Praxis häufig gar keine Rolle.
Antworten