Python çoklu okumayı destekliyor mu? Yürütme süresini hızlandırabilir mi?


99

Çoklu okumanın Python'da çalışıp çalışmadığı konusunda biraz kafam karıştı.

Bununla ilgili birçok soru olduğunu biliyorum ve çoğunu okudum, ama yine de kafam karıştı. Kendi deneyimlerimden biliyorum ve başkalarının kendi cevaplarını ve örneklerini burada StackOverflow'da yayınladığını gördüm ki, çoklu iş parçacığı gerçekten Python'da mümkündür. Öyleyse neden herkes Python'un GIL tarafından kilitlendiğini ve aynı anda yalnızca bir iş parçacığının çalışabileceğini söyleyip duruyor? Açıkça çalışıyor. Yoksa burada anlamadığım bir ayrım mı var?

Birçok poster / yanıtlayıcı, birden fazla çekirdek kullanılmadığı için iş parçacığı oluşturmanın sınırlı olduğunu belirtmeye devam ediyor. Ama yine de yararlı olduklarını söyleyebilirim çünkü eşzamanlı olarak çalışıyorlar ve böylece birleşik iş yükünü daha hızlı hallediyorlar. Demek istediğim, başka türlü neden bir Python iş parçacığı modülü olsa bile?

Güncelleme:

Şimdiye kadarki tüm cevaplar için teşekkürler. Anladığım kadarıyla çoklu iş parçacığı sadece bazı IO görevleri için paralel olarak çalışacak, ancak CPU'ya bağlı çoklu çekirdekli görevler için bir seferde yalnızca bir tane çalıştırabilir.

Bunun pratik anlamda benim için ne anlama geldiğinden tam olarak emin değilim, bu yüzden çoklu iş parçacığı ile okumak istediğim görev türüne bir örnek vereceğim. Örneğin, çok uzun bir dizge listesi üzerinden döngü yapmak istediğimi ve her liste öğesinde bazı temel dize işlemlerini yapmak istediğimi varsayalım. Listeyi bölersem, döngü / dize kodum tarafından işlenecek her bir alt listeyi yeni bir iş parçacığına gönderir ve sonuçları bir sıraya geri gönderirsem, bu iş yükleri yaklaşık olarak aynı anda çalışır mı? En önemlisi, bu teorik olarak betiği çalıştırmak için gereken süreyi hızlandıracak mı?

Başka bir örnek, PIL kullanarak dört farklı resmi dört farklı iş parçacığında oluşturabilir ve kaydedebilirsem ve bu, resimleri birbiri ardına işlemekten daha hızlı olabilir mi? Sanırım bu hız bileşeni, doğru terminolojiden ziyade gerçekten merak ettiğim şey.

Çoklu işlem modülünü de biliyorum, ancak şu anda ana ilgi alanım küçük-orta görev yükleri (10-30 saniye) ve bu yüzden çoklu kullanımın daha uygun olacağını düşünüyorum çünkü alt işlemlerin başlatılması yavaş olabilir.


4
Bu oldukça yüklü bir soru. Ben de cevap yalan düşünüyorum Ne sen ipler yapmak istiyorum. Çoğu durumda GIL, 1'den fazla iş parçacığının aynı anda çalışmasını engeller. Bununla birlikte, paralel olarak yapılabilmesi için GIL'in serbest bırakıldığı (örneğin bir dosyadan okuma) birkaç durum vardır. Ayrıca GIL'in Cpython'un (en yaygın uygulama) bir uygulama ayrıntısı olduğuna dikkat edin . Başka hiçbir python uygulamasında (Jython, PyPy, vb.) GIL (AFAIK) yoktur
mgilson

2
@mgilson PyPy'de GIL var.

2
@delnan - Doğru görünüyorsun. Teşekkürler.
mgilson

1
"alt işlemlerin başlatılması yavaş olabilir" - yürütülmeye hazır bir görev havuzu oluşturabilirsiniz. Ek yük, kabaca, görevin çalışmaya başlaması için gereken verileri serileştirmek / seri durumdan çıkarmak için gereken süre ile sınırlı olabilir.
Brian Cain

1
@KarimBahgat, tam olarak bunu kastediyorum.
Brian Cain

Yanıtlar:


139

GIL diş açmayı engellemez. GIL'in yaptığı tek şey, bir seferde yalnızca bir iş parçacığının Python kodunu çalıştırdığından emin olmaktır; kontrol hala iş parçacıkları arasında geçiş yapıyor.

O halde GIL'in engellediği şey, iş parçacıkları paralel olarak çalıştırmak için birden fazla CPU çekirdeği veya ayrı CPU'lar kullanmaktır.

Bu yalnızca Python kodu için geçerlidir. C uzantıları, birden çok C kodu iş parçacığına ve bir Python iş parçacığının birden çok çekirdekte çalışmasına izin vermek için GIL'i serbest bırakabilir ve çıkarır. Bu, select()yuva okuma ve yazma çağrıları gibi çekirdek tarafından kontrol edilen G / Ç'ye kadar uzanır ve Python'un çok iş parçacıklı çok çekirdekli bir kurulumda ağ olaylarını makul ölçüde verimli bir şekilde işlemesini sağlar.

Daha sonra birçok sunucu dağıtımının yaptığı şey, işletim sisteminin CPU çekirdeklerinizi maksimum düzeyde kullanmak için işlemler arasındaki zamanlamayı yönetmesine izin vermek için birden fazla Python sürecini çalıştırmaktır. Ayrıca kullanabilirsiniz multiprocessingkütüphane , bir kod temeli ve ana süreçten birden süreçler boyunca paralel işleme işlemek o takım elbise eğer kullanım durumları için.

GIL'in yalnızca CPython uygulaması için geçerli olduğunu unutmayın; Jython ve IronPython, farklı bir iş parçacığı uygulaması kullanır (sırasıyla yerel Java VM ve .NET ortak çalışma zamanı iş parçacıkları).

Güncellemenizi doğrudan ele almak için: Saf Python kodu kullanarak paralel yürütmeden hız artışı elde etmeye çalışan herhangi bir görev, iş parçacıklı Python kodu bir seferde yürütülen bir iş parçacığına kilitlendiğinden hızlanma görmeyecektir. Bununla birlikte, C uzantılarını ve G / Ç'yi karıştırırsanız (PIL veya numpy işlemleri gibi) ve herhangi bir C kodu, bir aktif Python iş parçacığı ile paralel olarak çalışabilir .

Python iş parçacığı, duyarlı bir GUI oluşturmak veya G / Ç'nin Python kodundan daha fazla darboğaz olduğu durumlarda birden çok kısa web isteğini işlemek için harikadır. Hesaplama açısından yoğun Python kodunu paralelleştirmek, multiprocessingbu tür görevler için modüle bağlı kalmak veya özel bir harici kitaplığa delege etmek için uygun değildir.


Teşekkürler @MartijnPieters, o zaman "hayır" olan for-loop gibi kodu hızlandırmak için iş parçacığının kullanılıp kullanılamayacağı konusundaki soruma daha net bir cevabım var. Belki siz veya bir başkası, iş parçacığının GIL tarafından paralel ve dolayısıyla daha hızlı çalışmasına izin verileceği ortak modüllerin / kodların / işlemlerin bazı özel örneklerini sağlayan kabul edebileceğim yeni bir cevap yazabilir (örneğin, bu G / Ç ve ağ örnekleri / bahsedilen soket okuma işlemleri ve Python'da çoklu okumanın yararlı olduğu diğer durumlar). Belki de yaygın çok iş parçacıklı kullanımların güzel bir listesi ve mümkünse bazı programlama örnekleri?
Karim Bahgat

4
Hayır, böyle bir cevabın çok yardımcı olacağını düşünmüyorum; Dürüst olmak gerekirse. Hiçbir zaman kapsamlı bir liste oluşturamazsınız, ancak temel kural, herhangi bir G / Ç'nin (dosya okuma ve yazma, ağ soketleri, kanallar) C'de işlenmesi ve birçok C kitaplığının da GIL'i kendi işlemler, ancak bunu sizin için belgelemek kütüphanelerin görevidir.
Martijn Pieters

1
Benim hatam, şimdiye kadar güncellenmiş cevabınızı görmediniz, burada bazı güzel iplik kullanımı örnekleri verdiniz. Bunlar arasında (doğru beni Im yanlış ise) ağ programlama (örn urllib.urlopen()?), Bir Python GUI içinde bir Python komut dosyası arama ve birden çok PIL (örn çağırarak Image.transform()) ve numpy (örn numpy.array()ipliklerle) işlemleri. Ve yorumunuzda, dosyaları okumak için birden çok iş parçacığı kullanmak (örneğin f.read()?) Kapsamlı bir listenin mümkün olmadığını biliyorum, sadece güncellemenizde verdiğiniz örnek türlerini istedim. Her iki durumda da cevabınızı kabul etti :)
Karim Bahgat

2
@KarimBahgat: Evet, urllib.urlopen()ağ soketlerini çağırır, soket giriş / çıkışını beklemek, iş parçacıkları değiştirmek ve başka bir şey yapmak için mükemmel bir fırsattır.
Martijn Pieters

4
Bu sorunla doğrudan ilgili olmasa da, bazen iş parçacığı oluşturmanın performansla ilgili olmadığını belirtmek gerekir; kodunuzu birden çok bağımsız yürütme dizisi olarak yazmak daha basit olabilir. Örneğin, arka plan müziği çalan bir iş parçacığınız, kullanıcı arayüzüne hizmet veren ve sonunda yapılması gereken, ancak acelesi olmayan hesaplamalardan uzaklaşan biri olabilir. UI runloop ile bir sonraki ses arabelleğini çalmayı sıraya koymaya çalışmak veya hesaplamanızı etkileşimi engellemeyecek kadar küçük parçalara bölmek, thread kullanmaktan çok daha zor olabilir.
abarnert

4

Evet. :)

Düşük seviye iplik modülüne ve daha yüksek seviye iplik geçirme modülüne sahipsiniz . Ancak, yalnızca çok çekirdekli makineleri kullanmak istiyorsanız , gitmenin yolu çok işlemcidir .

Dokümanlardan alıntı :

CPython'da, Global Yorumlayıcı Kilidi nedeniyle, aynı anda yalnızca bir iş parçacığı Python kodunu çalıştırabilir (bazı performans odaklı kütüphaneler bu sınırlamanın üstesinden gelse bile). Uygulamanızın çok çekirdekli makinelerin hesaplama kaynaklarından daha iyi yararlanmasını istiyorsanız, çoklu işlemeyi kullanmanız önerilir. Ancak, birden çok G / Ç bağlantılı görevi aynı anda çalıştırmak istiyorsanız, iş parçacığı yine de uygun bir modeldir.


3

Python'da İş Parçacığı Oluşturmaya İzin Verilir, tek sorun GIL'in her seferinde yalnızca bir iş parçacığının çalıştırıldığından emin olmasıdır (paralellik yok).

Yani temelde, hesaplamayı hızlandırmak için kodu çoklu iş parçacığı yapmak istiyorsanız, bir seferde sadece bir iş parçacığı çalıştırıldığı için hızlandırmaz, ancak örneğin bir veritabanıyla etkileşim için kullanırsanız, bu hızlanacaktır.


0

Poster için hissediyorum çünkü cevap her zaman "ne yapmak istediğinize bağlıdır". Bununla birlikte, python'da paralel hızlanma, çoklu işlem için bile deneyimime göre her zaman korkunç olmuştur.

Örneğin bu eğiticiye göz atın (google'da ikinci en üst sonuç): https://www.machinelearningplus.com/python/parallel-processing-python/

Bu kodun etrafına zamanlamaları koydum ve havuz haritası işlevi için işlem sayısını (2,4,8,16) artırdım ve aşağıdaki kötü zamanlamaları aldım:

serial 70.8921644706279 
parallel 93.49704207479954 tasks 2
parallel 56.02441442012787 tasks 4
parallel 51.026168536394835 tasks 8
parallel 39.18044807203114 tasks 16

kod: # başlangıçta dizi boyutunu artırın # hesaplama düğümümde 40 CPU var, bu yüzden burada yedeklenecek çok şey var

arr = np.random.randint(0, 10, size=[2000000, 600])
.... more code ....
tasks = [2,4,8,16]

for task in tasks:
    tic = time.perf_counter()
    pool = mp.Pool(task)

    results = pool.map(howmany_within_range_rowonly, [row for row in data])

    pool.close()
    toc = time.perf_counter()
    time1 = toc - tic
    print(f"parallel {time1} tasks {task}")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.