Printf() pod Windows.

Printf() pod Windows okazuje się sporo wolniejszy niż pod Linuksem.

printf_windowsParadoksalnie, jak widać powyżej, pythonowy print() jest ~3x szybszy od printf() z biblioteki standardowej Mingw-w64. Pod Linuksem na odwrót, 4x wolniejszy względem tego z libc.stdio:

print_linux


Poczytałem stdio.h i doszedłem do tego, że gdy  -D__USE_MINGW_ANSI_STDIO=1 używana jest funkcja printf() z Mingw (ponad 11 razy wolniejsza od printf() pod Linuksem) i tak jest „by default”.

Jeśli do gcc dodam  -D__USE_MINGW_ANSI_STDIO=0, używana jest wersja printf() z msvcrt.dll. Mimo, że ta funkcja ma swoje braki, różnica jest dość znaczna:

$ time ./fib_mingw_printf.exe 300000 > /dev/null

real 0m5.513s
user 0m0.000s
sys 0m0.031s

$ time ./fib_msvcrt_printf.exe 300000 > /dev/null

real 0m1.081s
user 0m0.015s
sys 0m0.000s


Jeszcze jedna ciekawostka: przekierowanie do /dev/null w MSYS2 (które jest prawdopodobnie mapowane na NUL) pod Windows jest powolne. Przekierowanie do pliku jest ~2x szybsze:

$ time ./fib_msvcrt_printf.exe 300000 > output.txt

real 0m0.562s
user 0m0.000s
sys 0m0.015s

(Pod Linuksem bez różnicy).


Na szczęście %timeit pozwala się przekonać, że kod generowany w tej samej wersji gcc pod Windows i pod Linuksem jest niemal tak samo wydajny.

Printf() pod Windows.

Msys2/Mingw-w64, Cython i Ipython Notebook.

Ipython Notebook z Mingw-w64-gcc/MSYS2 działa ładnie pod Windows (trzeba skompilować zeromq bo nie ma w repo mingw32 dla MSYS2, a bez tego pip nie skompiluje pyzmq, reszta instaluje się z pip).

cython_notebook

Super sprawa do testów Cythona i Pythona. Dla zainteresowanych: tutaj moja paczka mingw-w64-zeromq-4.0.5-1-any.pkg.tar.xz i PKGBUILD (jakby ktoś chciał inaczej zbudować, to trzeba budować przez wrapper makepkg-mingw). Reszta wymaganych pakietów: jinja2 i sqlite w repo MSYS2. pyzmq, tornado i pysegments instaluje się przez pip, który jest w paczce mingw-w64-i686-python3-pip w repo MSYS2.

W oficjalnym Pythonie nie udało mi się zmusić mingw-w64-gcc do kompilowania Cythona, choć wszystkie zależności notepada instalują się ładnie z pip.

Msys2/Mingw-w64, Cython i Ipython Notebook.

Python vs Cython vs PyPy.

Moich tendencyjnych eksperymentów ciąg dalszy. Zrezygnowałem z Numpy i jestem pod wrażeniem memoryviews w Cythonie. Poniższe to tak na prawdę test wydajności adresowania tablic jednowymiarowych.

Python:

import sys
import random

def fib(n):
    a, b = 0, 1
    for i in range(n):
        a, b = b, a + b
    return a

def showfib(n):
    r = []
    for i in range(n):
        t = fib(random.randint(0,49))
        r.append(t)
        print(t, end=" ")
    print()

if __name__ == '__main__':
    n=int(sys.argv[1])
    showfib(n)

Cython:

#cython: language_level=3, boundscheck=False

import sys
from libc.stdio cimport printf
from libc.stdlib cimport rand, srand
from cython.view cimport array
from time import time

cdef unsigned int fib(int m) nogil:
    cdef unsigned int a, b, i
    a, b = 0, 1
    for i in range(m):
        a, b = b, a + b
    return a

cdef showfib(n):
    cdef r = array(shape=(n,), itemsize=sizeof(unsigned int), format="I")
    cdef unsigned int [:] rv = r

    cdef int i
    cdef unsigned int t
    for i in range(n):
        t = fib(rand()%49)
        rv[i] = t
        printf("%u ", t)
    printf("\n")

if __name__ == '__main__':
    n=int(sys.argv[1])
    srand(int(time()))
    showfib(n)

Python-3.4.2:
$ time python fib.py 300000 > /dev/null
real 0m7.564s
user 0m7.543s
sys 0m0.013s

PyPy3-2.4 (portable):
$ time pypy fib.py 300000 > /dev/null
real 0m1.838s
user 0m1.813s
sys 0m0.020s

Cython-0.21.1:
$ time ./fib 300000 > /dev/null
real 0m0.189s
user 0m0.190s
sys 0m0.000s

Cython vs Python: 40,02 x szybciej.
Pypy vs Python: 4,11 x szybciej.
Cython vs PyPy: 9,72 x szybciej.

Python vs Cython vs PyPy.

Msys2 – toolchain idealny.

Idąc jak po nitce do kłębka, w końcu trafiłem na Msys2, czyli minimalny zestaw narzędzi systemowych, oparty o narzędzia uniksowe (głównie GNU) dla Windows.

msys2
Co się najbardziej rzuca w oczy w tym zrzucie? :) Ano pacman. Tak, ten sam pacman co w Arch Linuksie, którego używam na co dzień. Co za tym idzie? Wszystkie pakiety (kompilator, biblioteki, python, narzędzia) i kompletne środowisko GNU, są w repo projektu, więc aktualizacja (pod Windows!) to standardowe: pacman -Suy, a przebudowanie pakietu wg własnego uznania to edycja prostych, jak konstrukcja przysłowiowego cepa plików PKGBUILD.

Dla mnie Msys2 to brakujący element systemów Windowsowych.

Msys2 – toolchain idealny.

Cython – kompilacja programów w Pythonie mingw-w64 dla Windows.

Dawno nie miałem takiej motywacji do programowania. Cztery dni poszukiwań, grzebania w strzępkach dokumentacji i przykładach. W końcu się udało. Ale od początku. Postanowiłem trochę odświeżyć wiadomości o Pythonie i natrafiłem na Cythona, który mnie zafascynował, ale miałem problem z kodowaniem argumentów programów (sys.argv) do obiektów unikodowych pod Windows (argv w Windows jest w UTF-16, wchar_t **). W żaden sposób nie dało się ich dekodować do Unicode, a binarki dla Linuksa działały bez problemu. Pal licho argv ale to samo dotyczyło kodowania nazw plików. Przetestowałem kilkadziesiąt funkcji C-API Pythona i nic.  Skonwertowanie windowsowych szerokich argumentow (wchar_t **) argv z UTF-16 do UTF-8 wydawało się niemożliwe. Aż w jakimś archiwum ircowym trafiłem na wzmiankę, że bez flagi -municode mingw-gcc nie obsługuje pod Windows poprawnie kodowania linii cmd.

Co ciekawe, od początku wydawało mi się celowe generowanie entrypoint-a przez Cythona dla Windows jako:

int wmain(int argc, wchar_t **argv)

Okazuje się, że po dodaniu -municode do gcc mingw-w64 jako entrypoint traktuje właśnie wmain() i używa odpowiednich wersji funkcji z obsługą unicode. Niestety w mingw-gcc 4.8.1 nie ma opcji -municode i wiele osób pewnie z tego powodu ma problemy.  W gcc-4.8.1 z mingw-w64 działa jak należy, jest nawet o tym informacja na stronie projektu:

http://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/

Szkoda, że wcześniej tego nie znalazłem ;(.

aconv

Przykładowa konwersja i kompilacja wygląda tak, w Windows w MSYS:

/c/Python34/Scripts/cython.exe -3 --embed aconv.py
gcc aconv.c -I /c/mingw/python/include/python3.4m -L /c/mingw/python/lib/ -lpyth
on3.4m -municode -o aconv

Podsumowując. Bardzo mnie to cieszy. W Cythonie, poza niemal pełną zgodnością z Pythonem można includować nagłówki z bibliotek w C i bezpośrednio z nich korzystać w modułach pythonowych, z całą dobrocią typów C a pozwala na przyspieszenie kluczowych fragmentów kodu i kompilację nawet całych programów skonwertowanych do C. A w składni wygląda to jak Python.

Pełen respect dla twórców Cythona.

 

Cython – kompilacja programów w Pythonie mingw-w64 dla Windows.

Pypy, Cython, mingw-w64, static libpython3.4m.a.

Mój powrót do Pythona po kilkuletniej przerwie skończył się fascynacją. Po pierwsze Pypy – ale to szybkie i całkowicie zgodne z Pythonem! Po drugie Cython (opcja –embed) szok, udało mi się skompilować statyczne binarki skryptu aconv.py dla Linuksa i Windows (walczę jeszcze trochę z kodowaniem konsoli cmd w Windows, bo tu oczywiście są niemałe problemy z 16bit kodowaniem – muszę zrozumieć PyUnicode w Pythonie i Cythonie). Po trzecie: konfiguracja i działanie MSYS z mingw i mingw-w64 – niesamowity toolchain (instalowałem z win-builds), wszystko działa jak na Linuksie, trzeba tylko skonfigurować /c/mingw/msys/1.0/etc/fstab, czyli dodać c:\mingw /mingw, uruchamiać C:\MinGW\msys\1.0>msys.bat a potem konfigurację mingw-w64 (uwaga na kropkę) . /c/mingw/msys/1.0/opt/windows_32/bin/win-builds-switch 32 najlepiej dodać do ~/.bash_profile, ścieżka do gcc z mingw-w64 i bibliotek będzie się sama ustawiać po odpaleniu msys.bat.

Kompilację Pythona 3.4.2 ze statyczną i dynamiczną biblioteką libpython3.4m udostępniłem tutaj). Zbudowany po nałożeniu łatek z repo mingw-w64 i wykonaniu autoreconf-2.68.

W powyższym zipie zmodyfikowałem python/include/pyconfig.h w taki sposób, aby po dodaniu do wywołania gcc opcji -DPy_ENABLE_STATIC -static linkował z libpython3.4m.a, a bez tych opcji z libpython3.4m.dll:

#if defined Py_ENABLE_STATIC
#undef Py_ENABLE_SHARED
#else
#define Py_ENABLE_SHARED 1
#endif

PS: Aby interaktywny interpreter Pythona z tej kompilacji zadziałał, trzeba uruchomić polecenie:
. /c/mingw/msys/1.0/opt/windows_32/bin/win-builds-switch 32 w przeciwnym razie będzie mu brakować bibliotek.

Pypy, Cython, mingw-w64, static libpython3.4m.a.