Cython ile Python betiklerini geliştirme

0 Hisse senetleri
0
0
0
0

giriiş

Python günümüzün en popüler programlama dillerinden biri olabilir, ancak kesinlikle en verimlisi değil. Özellikle makine öğrenimi dünyasında, kullanıcılar Python'ın sunduğu kullanım kolaylığı uğruna verimlilikten ödün veriyor.

Ancak bu, işleri başka yollarla hızlandıramayacağınız anlamına gelmez. Cython, Python ile kolayca elde edilen işlevsellikten ödün vermeden Python betiklerinin hesaplama süresini azaltmanın kolay bir yoludur.

Bu eğitim, Python betiklerini hızlandırmak için Cython kullanımını tanıtacaktır. Basit ama hesaplama açısından maliyetli bir görevi ele alacağız: 1 milyar sayıdan oluşan bir Python listesini yineleyen ve toplayan bir for döngüsü oluşturmak. Kaynak kısıtlaması olan cihazlarda kod çalıştırırken zaman çok önemli olduğundan, bu konuyu Raspberry Pi (RPi) üzerinde Python kodunu Cython ile nasıl uygulayacağımızı ele alarak inceleyeceğiz. Cython, hesaplama hızında önemli bir fark yaratır; tıpkı bir lazy ve bir yozo gibi.

Cython ile Python betiklerini optimize etmek için ön koşullar

  • Python'un temel bilgileri: Python sözdizimi, fonksiyonları, veri tipleri ve modülleri hakkında bilgi sahibi olmak.
  • Temel C/C++ kavramlarını anlayın: İşaretçiler, veri tipleri ve kontrol yapıları gibi temel C veya C++ kavramlarına aşinalık.
  • Python geliştirme ortamı: Python'ı (tercihen Python 3.x) pip gibi bir paket yöneticisiyle kurun.
  • Cython'u yükleyin: Cython'u pip install cython komutuyla yükleyin.
  • Terminal/Komut Satırı Bilgisi: Terminal veya komut satırında gezinme ve komutları yürütme konusunda temel beceri.

Bu ön koşullar, Cython kullanarak Python kodunuzu optimize etmeye başlamanıza yardımcı olacaktır.

Python ve CPython

Birçok kişi, Python gibi dillerin aslında başka dillerde de uygulandığının farkında değildir. Örneğin, Python'ın C dilindeki uygulaması CPython olarak bilinir. Bunun Cython'dan farklı olduğunu unutmayın. Farklı Python uygulamaları hakkında daha fazla bilgi için bu makaleyi okuyabilirsiniz.

Python'un varsayılan ve en popüler uygulaması C-Python'dur. Bunu kullanmanın önemli bir avantajı vardır. C derlenmiş bir dildir ve kodu, doğrudan merkezi işlem birimi (CPU) tarafından çalıştırılan makine koduna dönüştürülür. Şimdi, C derlenmiş bir dilse, Python'ın da aynı olduğu anlamına mı geliyor diye sorabilirsiniz.

C'deki (Cpython) 100% Python uygulaması derlenmez veya yorumlanmaz. Aslında, hem derleme hem de yorumlama bir Python betiğinin çalıştırılması sürecinde gerçekleşir. Bunu açıklığa kavuşturmak için, bir Python betiğinin çalıştırılmasında yer alan adımlara bakalım:

  1. Bayt kodu üretmek için CPython kullanarak kaynak kodunu derleme
  2. CPython yorumlayıcısı tarafından bayt kodu yorumlanması
  3. CPython yorumlayıcısının çıktısını CPython sanal makinesinde çalıştırma

Derleme işlemi, Cpython kaynak kodunu (.py dosyası) derleyip Cpython bayt kodunu (.pyc dosyası) ürettiğinde gerçekleşir. Cpython bayt kodu daha sonra Cpython yorumlayıcısı tarafından yorumlanır ve çıktı Cpython sanal makinesinde yürütülür. Yukarıdaki adımlarda gösterildiği gibi, bir Python betiğini çalıştırma süreci hem derlemeyi hem de yorumlamayı içerir.

Cpython derleyicisi bayt kodunu yalnızca bir kez üretir, ancak yorumlayıcı kod her çalıştırıldığında çağrılır. Bayt kodunu yorumlamak genellikle uzun sürer. Eğer bir yorumlayıcı kullanmak yürütmeyi yavaşlatıyorsa, neden kullanasınız ki? Bunun temel nedeni, yorumlayıcının Python'ı farklı işletim sistemlerinde kullanılabilir hale getirmesidir. Bayt kodu, CPU üzerinde çalışan Cpython sanal makinesindeki makineden bağımsız olarak çalıştırıldığı için, herhangi bir değişiklik yapılmadan farklı makinelerde çalıştırılabilir.

Yorumlayıcı kullanılmazsa, Cpython derleyicisi doğrudan CPU'da çalışan makine kodu üretir. Farklı platformların farklı talimatları olduğundan, kod farklı platformlarda çalışamaz.

Kısacası, bir derleyici kullanmak süreci hızlandırır, ancak yorumlayıcı kodu platformlar arası hale getirir. Dolayısıyla, Python'un C'den daha yavaş olmasının nedenlerinden biri de yorumlayıcı kullanımıdır. Derleyicinin yalnızca bir kez çalıştığını, ancak yorumlayıcının kod her çalıştırıldığında çalıştığını unutmayın.

Python, C'den çok daha yavaştır, ancak birçok programcı kullanımı çok daha kolay olduğu için onu tercih eder. Python, programcıdan birçok ayrıntıyı gizler ve bu da sinir bozucu hata ayıklama işlemlerini önleyebilir. Örneğin, Python dinamik olarak yazılmış bir dil olduğundan, koddaki her değişkenin türünü belirtmeye gerek yoktur; Python bunu otomatik olarak çıkarır. Buna karşılık, statik olarak yazılmış dillerde (C, C++ veya Java gibi), aşağıda gösterildiği gibi değişken türlerini belirtmeniz gerekir.

int x = 10
string s = "Hello"

Aşağıdaki Python uygulamasıyla karşılaştırın:

Dinamik yazım, kodlamayı kolaylaştırır, ancak doğru veri türünü bulmak için makineye daha fazla yük bindirir. Bu da yürütme sürecini yavaşlatır.

x = 10
s = "Hello"

Genel olarak, Python gibi "üst düzey" diller geliştiriciler için çok daha kolay anlaşılır. Ancak kod yürütülürken, düşük düzeyli talimatlara dönüştürülmesi gerekir. Bu dönüştürme işlemi daha fazla zaman alır ve bu süre kullanım kolaylığı uğruna feda edilir.

Zaman önemliyse, düşük seviyeli komutlar kullanmalısınız. Yani, ön uç olan Python'da kod yazmak yerine, arka planda Python'ın C ile geliştirdiği CPython'u kullanabilirsiniz. Ancak bunu yaparsanız, Python'da değil, C'de programlıyormuş gibi hissedersiniz.

CPython çok daha karmaşıktır. CPython'da her şey C'de uygulanır. Kodlama yaparken C'nin karmaşıklığından kaçmanın bir yolu yoktur. Bu yüzden birçok geliştirici Cython'u tercih eder. Peki Cython, CPython'dan nasıl farklıdır?

Cython'un farkı nedir?

Yukarıda tanımlandığı gibi, Cython her iki dünyanın da en iyisini sunan bir dildir: hız ve kullanım kolaylığı. Python'da normal kod yazmaya devam edebilirsiniz, ancak yürütme süresini kısaltmak için Cython, Python kodunun bazı kısımlarını C ile değiştirmenize olanak tanır. Böylece, her iki dili de tek bir dosyada birleştirmiş olursunuz. Python'daki her şeyin Cython'da da geçerli olduğunu, ancak bazı sınırlamalarla birlikte olduğunu unutmayın.

Normal bir Python dosyası .pyx uzantısına sahipken, bir Cython dosyası .pyx uzantısına sahiptir. Aynı Python kodu .pyx dosyalarının içine de yazılabilir, ancak bu dosyalar Cython kodunu kullanmanıza da olanak tanır. Python kodunu bir .pyx dosyasına yerleştirmenin, Python kodunu doğrudan çalıştırmaktan daha hızlı olabileceğini, ancak değişken türlerini belirtmek kadar hızlı olmayacağını unutmayın. Bu nedenle, bu eğitimin odak noktası yalnızca bir .pyx dosyasına Python kodu yazmak değil, aynı zamanda daha hızlı çalışmasını sağlayacak değişiklikler yapmaktır. Bu, programlamaya biraz karmaşıklık katsa da çok zaman kazandırır. C programlama konusunda biraz deneyiminiz varsa, bu sizin için daha kolay olacaktır.

Basit Python kodunun sitronlaştırılması

Python kodunu Cython'a dönüştürmek için öncelikle . uzantılı bir dosya oluşturmanız gerekir. .pyx Uzantı yerine oluştur .py. Bu dosyanın içerisinde normal Python kodu yazmaya başlayabilirsiniz (Cython'un kabul ettiği kodda bazı sınırlamalar olduğunu unutmayın; bunlar Cython belgelerinde açıklanmıştır).

Devam etmeden önce Cython'un yüklü olduğundan emin olun. Bunu aşağıdaki komutu kullanarak yapabilirsiniz.

pip install cython

.pyd/.so dosyasını oluşturmak için önce Cython dosyasını derlememiz gerekiyor. .pyd/.so dosyası, daha sonra içe aktaracağımız modülü temsil eder. Cython dosyasını derlemek için bir setup.py dosyası kullanılır. Bu dosyayı oluşturun ve içine aşağıdaki kodu ekleyin. .pyx dosyasını siyanojenize edecek Cython.Build.cythonize() fonksiyonunu çağırmak için distutils.core.setup() fonksiyonunu kullanacağız. Bu fonksiyon, siyanojenize etmek istediğiniz dosyanın yolunu kabul eder. Burada, setup.py dosyasının test_cython.pyx dosyasıyla aynı konumda olduğunu varsaydım.

import distutils.core
import Cython.Build
distutils.core.setup(
ext_modules = Cython.Build.cythonize("test_cython.pyx"))

Cython dosyasını oluşturmak için komut satırına aşağıdaki komutu girin. Komut satırının geçerli dizininin, setup.py dosyasıyla aynı dizin olması beklenir.

python setup.py build_ext --inplace

Bu komut tamamlandıktan sonra, .pyx dosyasının yanına iki dosya yerleştirilecektir. İlk dosyanın uzantısı .c, diğer dosyanın uzantısı ise .pyd (veya kullanılan işletim sistemine bağlı olarak benzer bir uzantı) olacaktır. Oluşturulan dosyayı kullanmak için test_cython modülünü içe aktarmanız yeterlidir; aşağıda görebileceğiniz gibi, "Merhaba Cython" mesajı doğrudan görüntülenecektir.

Python kodunu başarıyla Cytonize ettik. Bir sonraki bölümde, bir döngünün oluşturulduğu bir .pyx dosyasını Cytonize etmeye bakacağız.

"For" döngüsünün sitonize edilmesi“

Şimdi önceki görevimizi optimize edelim: 1 milyon sayıyı tarayan ve toplayan bir for döngüsü. Sadece döngünün verimliliğini inceleyerek başlayalım. Zaman modülü, yürütülmesinin ne kadar sürdüğünü tahmin etmek için devreye giriyor.

import time
t1 = time.time()
for k in range(1000000):
pass
t2 = time.time()
t = t2-t1
print("%.20f" % t)

Bir .pyx dosyasında, 3 çalıştırma için ortalama süre 0,0281 saniyedir. Kod, Core i7-6500U @ 2,5 GHz işlemci ve 16 GB DDR3 RAM'e sahip bir bilgisayarda çalışmaktadır.

Bunu, ortalama 0,0411 saniye olan tipik bir Python dosyasının yürütme süresiyle karşılaştırın. Bu, Cython'un yinelemeler için Python'dan yalnızca 1,46 kat daha hızlı olduğu anlamına gelir; for döngüsünü C hızında çalışacak şekilde değiştirmenize gerek kalmadan bile.

Şimdi toplama işlemini yapalım. Bunu yapmak için range() fonksiyonunu kullanacağız.

import time
t1 = time.time()
total = 0
for k in range(1000000):
total = total + k
print "Total =", total
t2 = time.time()
t = t2-t1
print("%.100f" % t)

Her iki betiğin de aynı değeri, yani 499999500000 değerini döndürdüğünü unutmayın. Python'da bu işlemin tamamlanması ortalama 0,1183 saniye (üç test boyunca) sürer. Cython'da ise 1,35 kat daha hızlıdır ve ortalama 0,0875 saniye sürer.

Şimdi döngünün 0'dan başlayıp 1 milyar sayı boyunca devam ettiği başka bir örneğe bakalım.

import time
t1 = time.time()
total = 0
for k in range(1000000000):
total = total + k
print "Total =", total
t2 = time.time()
t = t2-t1
print("%.20f" % t)

Cython betiği yaklaşık 85 saniyede (1,4 dakika), Python betiği ise yaklaşık 115 saniyede (1,9 dakika) tamamlandı. Her iki durumda da çok zaman alıyor. Böylesine basit bir görevi tamamlamak bir dakikadan fazla sürüyorsa Cython kullanmanın ne anlamı var? Bunun Cython'un değil, bizim hatamız olduğunu unutmayın.

Daha önce de belirtildiği gibi, Python kodunu bir Cython betiği (.pyx) içine yazmak bir iyileştirmedir, ancak çalışma zamanında önemli bir fark yaratmaz. Cython betiği içindeki Python kodunda bazı değişiklikler yapmamız gerekiyor. Yapmamız gereken ilk şey, kullanılan değişkenlerin veri türünü açıkça belirtmektir.

C veri tiplerini değişkenlere atama

Önceki kodda olduğu gibi, 5 değişken kullanılmıştır: toplam, k, t1, t2 ve t. Tüm bu değişkenlerin veri türleri kod tarafından dolaylı olarak çıkarıldığı için daha fazla zaman alır. Veri türünü çıkarsamak için harcanan zamandan tasarruf etmek adına, veri türünü C dilinden atayalım.

Toplam değişkeninin türü unsigned long long int'tir. Tüm sayıların toplamı bir tam sayı olduğu için tam sayıdır ve toplam her zaman pozitif olacağı için işaretsizdir. Peki neden long long? Tüm sayıların toplamı çok büyük olduğundan, değişkeni mümkün olduğunca büyük yapmak için long long kullanılır.

k değişkenine atanan veri türü int'tir ve kalan üç değişken t1, t2 ve t'ye float veri türü atanmıştır.

import time
cdef unsigned long long int total
cdef int k
cdef float t1, t2, t
t1 = time.time()
for k in range(1000000000):
total = total + k
print "Total =", total
t2 = time.time()
t = t2-t1
print("%.100f" % t)

Son print ifadesinde tanımlanan hassasiyetin 100 olarak ayarlandığını ve tüm bu sayıların sıfır olduğunu fark edin (bir sonraki görsele bakın). Cython'dan bekleyebileceğimiz şey budur. Python 1,9 dakikadan fazla sürerken, Cython hiç zaman almaz. Python'dan 1000 veya 100.000 kat daha hızlı olduğunu bile söyleyemem; yazdırılan zaman için farklı hassasiyetler denedim ve yine de hiçbir sayı görünmedi.

range() fonksiyonuna iletilen değeri tutmak için bir tamsayı değişkeni de oluşturabileceğinizi unutmayın. Bu, performansı daha da artıracaktır. Aşağıda, değerin tamsayı değişkeni maxval'da saklandığı yeni kod yer almaktadır.

import time
cdef unsigned long long int maxval
cdef unsigned long long int total
cdef int k
cdef float t1, t2, t
maxval=1000000000
t1=time.time()
for k in range(maxval):
total = total + k
print "Total =", total
t2=time.time()
t = t2-t1
print("%.100f" % t)

Cython kullanarak Python scriptlerinin performansının nasıl artırılacağını gördüğümüze göre şimdi bunu Raspberry Pi'ye (RPi) uygulayalım.

Kişisel bilgisayardan Raspberry Pi erişilebilirliği

Raspberry Pi'nizi ilk kez kullanıyorsanız, hem bilgisayarınızı hem de RPi'nizi bir ağa bağlamanız gerekecektir. Bunu, her iki cihazı da IP adreslerini otomatik olarak atamak için DHCP (Dinamik Ana Bilgisayar Yapılandırma Protokolü) etkinleştirilmiş bir anahtara bağlayarak yapabilirsiniz. Ağ başarıyla oluşturulduktan sonra, RPi'nize atanan IPv4 adresine göre erişebilirsiniz. Peki, RPi'nizin hangi IPv4 adresine atandığını nasıl öğrenebilirsiniz? Endişelenmeyin, bir IP tarayıcı aracı kullanabilirsiniz. Bu eğitimde, Advanced IP Scanner adlı ücretsiz bir uygulama kullanacağım.

Bu uygulamanın kullanıcı arayüzü aşağıdaki gibidir. Bu uygulama, arama yapmak için bir dizi IPv4 adresi kabul eder ve etkin cihazlar hakkında bilgi döndürür.

Yerel ağınızdaki IPv4 adres aralığını girmeniz gerekir. Bu aralığı bilmiyorsanız, bilgisayarınızın IPv4 adresini bulmak için Windows'ta ipconfig komutunu (veya Linux'ta ifconfig komutunu) çalıştırmanız yeterlidir (aşağıdaki şekilde gösterildiği gibi). Benim durumumda, bilgisayarımın Wi-Fi bağdaştırıcısına atanan IPv4 adresi 192.168.43.177 ve alt ağ maskesi 255.255.255.0'dır. Bu, ağdaki IPv4 adres aralığının 192.168.43.1 ile 192.168.43.255 arasında olduğu anlamına gelir. Şekilde gösterildiği gibi, 192.168.43.1 IPv4 adresi Ağ Geçidine atanmıştır. Bu aralıktaki son IPv4 adresi olan 192.168.43.255'in yayın mesajları için ayrıldığını unutmayın. Yani arama yapmanız gereken aralık 192.168.43.2'de başlayıp 192.168.43.254'te bitiyor.

Bir sonraki şekilde gösterilen tarama sonucuna göre, RPi'ye atanan IPv4 adresi 192.168.43.63'tür. Bu IPv4 adresi, Güvenli Kabuk (SSH) oturumu kurmak için kullanılabilir.

SSH oturumu oluşturmak için MobaXterm adlı ücretsiz bir yazılım kullanacağım. Bu programın kullanıcı arayüzü aşağıdaki gibidir.

Bir SSH oturumu oluşturmak için sol üst köşedeki Oturum düğmesine tıklamanız yeterlidir. Aşağıda gösterildiği gibi yeni bir pencere açılacaktır.

Bu pencereden, sol üst köşedeki SSH düğmesine tıklayarak aşağıda gösterilen pencereyi açın. Raspberry Pi'nizin IPv4 adresini ve kullanıcı adını (varsayılan olarak pi) girin ve ardından oturumu başlatmak için Tamam'a tıklayın.

Tamam düğmesine tıkladıktan sonra, parolanızı girmenizi isteyen yeni bir pencere açılacaktır. Varsayılan parola raspberrypi'dir. Giriş yaptıktan sonra aşağıdaki pencere açılacaktır. Sol bölme, Raspberry Pi dizinlerinizde kolayca gezinmenize yardımcı olacaktır. Ayrıca komutları girmek için bir komut satırı da mevcuttur.

Cython'u Raspberry Pi ile Kullanma

Önceki örnekteki kodu yazmak için yeni bir dosya oluşturun ve uzantısını .pyx olarak değiştirin. Sol panel çubuğunda yeni dosya ve dizin oluşturma seçenekleri bulunur. Aşağıdaki görselde gösterildiği gibi, bunu kolaylaştırmak için yeni dosya simgesini kullanabilirsiniz. Raspberry Pi'nin kök dizininde test_cython.pyx adlı bir dosya oluşturdum.

Dosyayı açmak için çift tıklayın, kodu yapıştırın ve kaydedin. Ardından, daha önce bahsettiğimiz gibi setup.py dosyasını oluşturmamız gerekiyor. Ardından, Cython betiğini oluşturmak için aşağıdaki komutu çalıştırmamız gerekiyor.

python3 setup.py build_ext --inplace

Bu komut başarıyla tamamlandıktan sonra, çıktı dosyalarını aşağıdaki şekilde gösterildiği gibi sol panelde bulabilirsiniz. Artık Windows kullanmadığımız için, içe aktarılacak modülün uzantısının artık .so olduğunu unutmayın.

Şimdi Python'u etkinleştirip modülü aşağıda gösterildiği gibi içe aktaralım. Burada da bilgisayardakiyle aynı sonuçlar elde ediliyor; neredeyse sıfır zaman alıyor.

Sonuç

Bu eğitimde, Python betiklerini çalıştırmanın hesaplama süresini azaltmak için Cython'un nasıl kullanılacağı ele alındı. Döngü kullanımına dair bir örnek göstereceğiz. için 1 milyar sayıdan oluşan bir Python listesinin tüm öğelerini eklemeyi inceledik ve değişkenleri bildirerek ve bildirmeden yürütme sürelerini karşılaştırdık. Bu işlem saf Python'da yaklaşık iki dakika sürerken, Cython kullanarak ve statik değişkenler bildirerek neredeyse hiç zaman kaybetmeden tamamlanır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Ayrıca Şunları da Beğenebilirsiniz