Performansı artırmak için neden çoklu okuma sıklıkla tercih edilir?


23

Bir sorum var, programcıların neden eşzamanlılık ve genel olarak çok iş parçacıklı programları sevdiği görünüyor.

Burada 2 ana yaklaşım düşünüyorum:

  • temel olarak sinyallere dayalı bir eşzamansız yaklaşım ya da örneğin yeni C # 5.0 gibi birçok makale ve dilde çağrıldığı gibi eşzamansız bir yaklaşım ve boru hattınızın politikasını yöneten bir "eşlik ipliği"
  • eşzamanlı bir yaklaşım veya çok iş parçacıklı bir yaklaşım

Ben sadece buradaki donanımı ve en kötü durum senaryosunu düşündüğümü söyleyeceğim ve bu 2 paradigmayı kendim test ettim, async paradigması, insanların neden% 90'ını neden alamadığım noktasında bir kazanan. Bir şeyleri hızlandırmak veya kaynaklarını iyi bir şekilde kullanmak istediklerinde çoklu kullanım hakkında konuşun.

İşlemcinin içinde bir bellek denetleyicisi bulunmayan Intel dört çekirdekli eski bir makinede çoklu iş parçacıklı programları ve eşzamansız programları test ettim, bellek tamamen anakart tarafından yönetiliyor, bu durumda performanslar korkunç. Çok dişli uygulama, 3-4-5 gibi nispeten az sayıda diş bile sorun olabilir, uygulama tepkisiz ve sadece yavaş ve nahoş oluyor.

Öte yandan, iyi bir eşzamansızlık yaklaşımı, muhtemelen daha hızlı değildir ancak en kötüsü de değildir, başvurum sadece sonucu bekler ve askıda kalmaz, tepki verir ve çok daha iyi bir ölçeklenme olur.

İplik dünyasında bir bağlam değişikliğinin gerçek dünya senaryosunda o kadar ucuz olmadığını, özellikle hesaplanması için birbirleri arasında dolaşıp değiş tokuş yapması gereken 2'den fazla ipliğiniz olduğunda aslında oldukça pahalı olduğunu keşfettim.

Modern CPU'larda durum o kadar da farklı değil, tümleşik bellek denetleyicisi, ancak benim açımdan bir x86 işlemcilerin temelde bir seri makine olduğu ve bellek denetleyicisinin anakart üzerinde harici bir bellek denetleyicisine sahip eski makine ile aynı şekilde çalıştığı . Bağlam anahtarı hala uygulamamda önemli bir maliyet ve entegre edilmiş bellek denetleyicisinin ya da yeni CPU'nun 2 çekirdekten fazlası olması benim için pazarlık yapmıyor.

Eş zamanlı yaklaşımı deneyimlediklerim için teoride iyidir ancak pratikte o kadar da iyi değildir, donanımın dayattığı bellek modeliyle bu paradigmayı iyi kullanmak zordur, ayrıca kullanımdan çok sayıda konuyu ortaya çıkarır. veri yapılarımın çoklu iş parçacıklarının birleştirilmesine.

Ayrıca, her iki paradigma da, görev veya iş belirli bir zamanda yapılacaksa, onları işlevsel açıdan gerçekten benzer kılan herhangi bir güvenlik önermez.

X86 bellek modeline göre, neden insanların çoğunluğu sadece bir eşzamansız yaklaşım değil, C ++ ile eşzamanlılık kullanmasını önermektedir? Ayrıca neden bağlam anahtarının hesaplamanın kendisinden daha pahalı olduğu bir bilgisayarın en kötü durum senaryosunu dikkate almıyorsunuz?


2
Karşılaştırma yapmanın bir yolu, JavaScript dünyasına bakmak, iş parçacığı olmaması ve geri çağrıları kullanarak her şey agresif bir şekilde eşzamansız olmaktır. İşe yarıyor ama kendi sorunları var.
Robotu

2
@StevenBurnap Web çalışanları ne diyorsunuz?
user16764

2
"3-4-5 gibi nispeten az sayıda iplik bile sorun olabilir, uygulama yanıt vermiyor ve sadece yavaş ve nahoş oluyor." => Zayıf tasarım / iş parçacığının uygunsuz kullanımı nedeniyle olabilir. Dişlileriniz veri alışverişinde bulunduğunda genellikle bu tür bir durum görürsünüz; bu durumda çoklu iş parçacığı doğru cevap olmayabilir ya da verileri yeniden bölümlendirmeniz gerekebilir.
assylias

1
@assylias UI iş parçacığında önemli bir yavaşlama görmeniz, iş parçacığı arasında aşırı miktarda kilitlenme olduğunu gösterir. Kötü bir uygulamanız var ya da yuvarlak bir çukura bir kare dübel çakmaya çalışıyorsunuz.
Evan Plaice

5
“Programcılar eşzamanlılık ve genel olarak çok iş parçacıklı programları seviyor gibi görünüyor” diyorsunuz. "Programcılar bundan nefret ediyor" derdim ... ... ama çoğu zaman yapılacak tek faydalı şey ...
johannes

Yanıtlar:


34

Sen, çoklu çekirdek / procesors var kullanmak onları

Async , ağır IO'ya bağlı işlem yapmak için en iyisidir, fakat ya ağır CPU'ya bağlı işleme?

Sorun, uzun süreli bir işlemde tek iş parçacıklı kod blokları (örneğin sıkışmış) olduğunda ortaya çıkar. Örneğin, bir kelime işlemci belgesi yazdırırken, iş gönderilinceye kadar tüm uygulamanın donmasına neden olacağını hatırlayın. Uygulama dondurma işlemi, CPU yoğun bir işlem sırasında tek iş parçacıklı bir uygulamanın engellenmesinin bir yan etkisidir.

Çok iş parçacıklı bir uygulamada, CPU yoğun görevler (örneğin bir yazdırma işi) arka plan çalışan iş parçacığına gönderilebilir ve böylece UI iş parçacığı serbest bırakılır.

Benzer şekilde, çok işlemli bir uygulamada iş, mesajlaşma (örneğin IPC, soketler, vb.), Özellikle işleri işlemek için tasarlanmış bir alt işleme gönderilebilir.

Uygulamada, zaman uyumsuz ve çok iş parçacıklı / işlem kodunun her birinin yararları ve sakıncaları vardır.

Bu eğilimi büyük bulut platformlarında görebilirsiniz, çünkü CPU'ya bağlı işlemler için uzmanlaşmış örnekler ve IO'ya bağlı işlemler için uzmanlaşmış örnekler sunar.

Örnekler:

  • Depolama (eski Amazon S3, Google Cloud Drive) CPU'ya bağlı
  • Web Sunucuları IO'ya bağlı (Amazon EC2, Google App Motoru)
  • Veritabanlarının her ikisi de, yazma / endeksleme için CPU'ya ve okumalar için IO'ya bağlı

Perspektife koymak için ...

Bir web sunucusu, güçlü bir şekilde IO'ya bağlı olan bir platform için mükemmel bir örnektir. Bağlantı başına bir iş parçacığı atanan çok iş parçacıklı bir web sunucusu, ölçeklendirilmez çünkü her iş parçacığı, paylaşılan kaynaklarda artan bağlam değiştirme ve iş parçacığı kilitleme miktarı nedeniyle daha fazla ek yüke neden olur. Oysa bir zaman uyumsuz bir web sunucusu tek bir adres alanı kullanırdı.

Benzer şekilde, video kodlama konusunda uzmanlaşmış bir uygulama çok iş parçacıklı bir ortamda çok daha iyi çalışacaktır çünkü içerdiği ağır işlem ana iş parçacığını iş bitinceye kadar kilitleyecektir. Bunu azaltmanın yolları var, ancak kuyruğu yöneten tek bir iş parçacığı, temizliği yöneten ikinci bir iş parçacığı ve ağır işlemeyi yöneten bir iş parçacığı havuzu olması çok daha kolay. Dişler arasındaki iletişim, yalnızca görevler atandığında / tamamlandığında gerçekleşir, böylece iplik kilitleme tepesi minimum düzeyde tutulur.

En iyi uygulama genellikle her ikisinin bir kombinasyonunu kullanır. Örneğin bir webapp, gelen isteklerin torrentini yönetmek için bir yük dengeleyici olarak nginx'i (yani tek bir iş parçacıklı olmayan), http isteklerini işlemek için benzer bir eşzamansız web sunucusu (ex Node.js) ve bir çok iş parçacıklı sunucu kullanabilir. yükleme / akış / kodlama içeriği, vb işlemek ...

Çok iş parçacıklı, çok işlemli ve eşzamansız modeller arasında yıllar boyunca birçok dini savaş yaşandı. En çok olduğu gibi, en iyi cevabın gerçekten olması gerektiği gibi, "bağlıdır."

GPU ve CPU mimarilerini paralel olarak kullanmayı haklı kılan aynı düşünce hattını izler. Konserde çalışan iki özel sistem, tek bir monolitik yaklaşımdan çok daha büyük bir iyileştirmeye sahip olabilir.

İkisi de daha iyi çünkü ikisinin de kullanımları var. İş için en iyi aracı kullanın.

Güncelleştirme:

Apache referansını kaldırdım ve küçük bir düzeltme yaptım. Apache, çekirdek düzeyindeki bağlam anahtarlama miktarını artıran her istek için bir süreç gerektiren bir çok işlemeli model kullanır. Ayrıca, bellek işlemler arasında paylaşılamadığından, her istek ek bir bellek maliyeti gerektirir.

Çoklu iş parçacığı ek bellek gerektiriyor, çünkü iş parçacığı arasındaki paylaşılan belleğe güveniyor. Paylaşılan hafıza, ek hafızanın üstünü kaldırır, ancak bağlam bağlamında geçişin cezasını almaya devam eder. Ek olarak - yarış koşullarının gerçekleşmemesini sağlamak için - iş parçacıkları arasında paylaşılan tüm kaynaklar için iş parçacığı kilitleri (bir seferde yalnızca bir iş parçasına özel erişim sağlayan) gerekir.

"Programcılar eşzamanlılık ve genel olarak çok iş parçacıklı programları seviyor gibi görünüyor." Çok iş parçacıklı programlama, zamanlarında önemli miktarda yapan herkes tarafından evrensel olarak korkutulur. Ölü kilitler (bir kaynak, her zaman sonlandırmayı engelleyen iki farklı kaynak tarafından yanlışlıkla yanlışlıkla kilitlendiğinde meydana gelen bir hata) ve yarış koşulları (programın yanlış sıralama nedeniyle rastgele yanlış sonucu rastgele çıkardığı), izlenmesi en zor olanlardan bazılarıdır. aşağı ve düzelt.

Update2:

IPC ile ilgili battaniye ifadesinin aksine ağ (yani soket) iletişiminden daha hızlı olması. Bu her zaman böyle değildir . Bunların genellemeler olduğunu ve uygulamaya özgü ayrıntıların sonuç üzerinde büyük bir etkisi olabileceğini unutmayın.


Neden bir programcı çok işlem görmeli? Demek istediğim, 1'den fazla işlemle, önemli bir ek yük getirebilecek bir çeşit süreçler arası iletişime de ihtiyacınız olduğunu varsayıyorum, bu, eski Windows programcısı gibi şeyler yapmanın bir yolu mu? çoklu işleme ne zaman gitmeliyim? Bu arada cevabınız için teşekkürler, zaman uyumsuz ve çok iş parçacıklı ne için iyi gerçekten iyi bir resim.
user1849534 15:12

1
Süreçler arası iletişimin genel ek yükü artıracağını varsayıyorsunuz. Bununla birlikte, işlem durumu değişmez ise veya yalnızca senkronizasyon işlemini başlatma / tamamlama işleminde gerçekleştirmesi gerekiyorsa. Paralel görevlerde daha etkili olabilir. Aktör paterni iyi bir örnektir ve eğer okumadıysanız, okumaya değer. akka.io
sylvanaar

1
@ user1849534 Birden fazla konu birbirleriyle paylaşılan hafıza + kilitleme veya IPC ile konuşabilir . Kilitlemek daha kolaydır, ancak bir hata yaparsanız hata ayıklamak daha zordur (örn. Kilitlenmemiş, kilitlenmemiş kilit). Çok sayıda çalışan iş parçacığınız varsa, kilitleme iyi ölçeklenemediğinden IPC en iyisidir . Her iki durumda da, çok dişli bir yaklaşım kullanıyorsanız, dişler arasında iletişimi / senkronizasyonu mutlak bir minimumda tutmak önemlidir (ek yükü en aza indirmek için).
Evan Plaice,

1
@ akka.io Tamamen haklısın. Takılabilirlik, kilitleme ek yükünü en aza indirmenin / ortadan kaldırmanın bir yoludur, ancak yine de bağlam anahtarlama zaman maliyetine maruz kalıyorsunuz. Değişmezliğin, iplik senkronizasyonu sorunlarını nasıl çözebileceği ile ilgili ayrıntıları içerecek şekilde cevabı genişletmek isterseniz, çekinmeyin. Örneklemem gereken ana nokta, zaman uyumsuz iletişimin çok iş parçacıklı / süreç ve bunun tersi yönünden belirgin bir avantaja sahip olduğu durumlardır.
Evan Plaice

(devam) Ancak, dürüst olmak gerekirse, çok sayıda CPU'ya bağlı işlem yeteneğine ihtiyacım olursa, oyuncu modelini atlar ve birden çok ağ düğümüne ölçeklendirme yeteneğine sahip olurdum. Bunun için gördüğüm en iyi çözüm, soket düzeyinde iletişim üzerinden 0MQ'nun görev ventilatör modelini kullanmak. Bakınız Şekil 5 @ zguide.zeromq.org/page:all .
Evan Plaice,

13

Microsoft'un eşzamansız yaklaşımı , çok iş parçacıklı programlama amaçlarının en yaygın olanı için iyi bir alternatiftir: IO görevlerine göre yanıt verme yeteneğini geliştirmek.

Bununla birlikte, zaman uyumsuz yaklaşımın performansı iyileştirme ya da CPU yoğun görevlere göre yanıt verme yeteneğini geliştirme yeteneğinin olmadığını fark etmek önemlidir.

Duyarlılık için Çoklu Okuma

Duyarlılık için çok iş parçacığı, bir programı ağır GÇ görevleri veya ağır hesaplama görevleri sırasında yanıt veren geleneksel yoldur. Dosyaları bir arka plan iş parçacığına kaydedersiniz, böylece kullanıcı sabit sürücünün görevini tamamlamasını beklemeden çalışmalarına devam edebilir. GÇ ipliği genellikle yazının bir bölümünün bitmesini bekler, bu nedenle bağlam anahtarları sık sık.

Benzer şekilde, karmaşık bir hesaplama yaparken, kullanıcı arabiriminin duyarlı kalmasını sağlamak için düzenli bağlam geçişine izin vermek istiyorsunuz ve kullanıcı programın çökeceğini düşünmüyor.

Buradaki amaç, genel olarak, farklı CPU'larda birden fazla iş parçacığı elde etmektir. Bunun yerine, yalnızca uzun süredir devam eden arka plan görevi ve UI arasında gerçekleşen bağlam anahtarlarının elde edilmesiyle ilgileniyoruz, böylece kullanıcı arabirimi arka plan görevi çalışırken kullanıcıyı güncelleyebilir ve yanıt verebilir. Genel olarak, kullanıcı arayüzü çok fazla CPU gücü kullanmaz ve iş parçacığı çerçevesi veya işletim sistemi genellikle onları aynı CPU'da çalıştırmaya karar verir.

Aslında bağlamsal anahtarlama maliyeti nedeniyle toplam performansı kaybediyoruz, ancak işlemcinin performansı bizim hedefimiz olmadığı için umursamıyoruz. Genelde ihtiyaç duyduğumuzdan daha fazla CPU gücüne sahip olduğumuzu biliyoruz ve bu yüzden çoklu okuma ile ilgili hedefimiz, kullanıcının zamanını boşa harcamamaksızın kullanıcı için bir görev yapmak.

"Asenkron" Alternatif

"Eşzamansız yaklaşım", bu resmi tek bir iş parçacığı içindeki bağlam anahtarları etkinleştirerek değiştirir. Bu, tüm görevlerimizin tek bir CPU üzerinde çalışacağını garanti eder ve daha az diş açma / temizleme ve dişler arasında daha az gerçek bağlam geçişi açısından mütevazı performans iyileştirmeleri sağlayabilir.

Bir ağ kaynağının alınmasını beklemek için yeni bir iş parçacığı oluşturmak yerine (örneğin, bir görüntüyü indirmek), görüntünün asynckullanılabilir awaithale geldiği ve bu arada çağıran yönteme yol açan bir yöntem kullanılır.

Buradaki ana avantaj, kilitleri ve senkronizasyonu hiç kullanmadığınız için kilitlenmeden kaçınma gibi konularla ilgili endişelenmenize gerek kalmaması ve programcının arka plan iş parçacığını ayarlama ve geri alma konusunda biraz daha az iş olması. UI güvenli bir şekilde güncellemek için sonuç geri geldiğinde UI iş parçacığında.

Teknik ayrıntılara çok fazla bakmadım ama izlenimim, zaman zaman hafif CPU etkinliği ile indirmeyi yönetmek, ayrı bir iş parçacığı için değil, UI etkinlik kuyruğundaki bir görev gibi, ve daha sonra indirme işlemi tamamlandığında, zaman uyumsuz yöntem bu olay kuyruğundan devam eder. Başka bir deyişle, await"ihtiyacım olan sonucun mevcut olup olmadığını kontrol et, beni bu konunun görev sırasına koyup koymadığını" söyleyen bir şey anlamına geliyor.

Bu yaklaşımın CPU yoğun bir görevin sorununu çözmeyeceğini unutmayın: bekleyecek bir veri yok, bu nedenle gerçek bir arka plan çalışan iş parçacığı oluşturmadan gerçekleşmesi gereken bağlam anahtarlarını alamıyoruz. Tabii ki, arka plan iş parçacığını başlatmak ve sonucu asenkron yaklaşımı kullanan bir programda, arka plan iş parçacığını başlatmak ve sonucu döndürmek için eşzamansız bir yöntem kullanmak yine de uygun olabilir.

Performans için Çoklu Okuma

“Performans” hakkında konuştuğunuz için, çoklu iş görmenin performans kazanımları için nasıl kullanılabileceğini tartışmak isterim, tek iş parçacıklı asenkron yaklaşımla tamamen imkansız olan bir şey.

Aslında, tek bir CPU üzerinde yeterli CPU gücüne sahip olmadığınız ve performans için çoklu okuma kullanmak istediğinizde, bunu yapmak genellikle zor olur. Öte yandan, eğer bir CPU yeterli işlem gücü yoksa, genellikle programınızın makul bir zaman diliminde başarmak istediğiniz şeyi yapmasını mümkün kılan tek çözümdür, bu da işi değerli kılar.

Önemsiz Paralellik

Tabii ki, bazen çok iş parçacıklı gerçek hızlanma kolay olabilir .

Çok sayıda bağımsız hesaplama yoğun göreviniz varsa (yani, girdi ve çıktı verileri sonucu belirlemek için yapılması gereken hesaplamalara göre çok küçük olan işler) varsa, o zaman bir iş parçacığı havuzu oluşturmak (uygun CPU sayısına göre uygun şekilde boyutlandırılır) ve bir ana iş parçacığının işi dağıtması ve sonuçları toplaması.

Performans İçin Pratik Çoklu Okuma

Kendimi çok fazla uzman olarak öne sürmek istemem, ama benim izlenimim, genel olarak, bugünlerde gerçekleşen performansa ilişkin en pratik okuyucunun önemsiz paralelliğe sahip bir uygulamadaki yerleri aramak ve çoklu iplikler kullanmak olduğu yönünde. yararları elde etmek için.

Herhangi bir optimizasyonda olduğu gibi, programınızın performansını belirledikten ve etkin noktaları belirledikten sonra optimizasyon yapmak genellikle daha iyi olur: Bir programın bu kısımda bir kısımda ve o kısımda başka bir kısımda çalışması gerektiğine karar vererek keyfi bir şekilde yavaşlatmak kolaydır. Öncelikle her iki parçanın da CPU zamanının önemli bir kısmını alıp almadığını belirlemek.

Ek bir iş parçacığı daha fazla kurulum / ayırma maliyeti ve daha fazla bağlam anahtarı veya daha fazla CPU arası iletişim maliyeti anlamına gelir. Ayrı bir CPU'daysa bu masrafları telafi etmek için yeterli iş yapmıyorsa ve duyarlılık nedenleriyle ayrı bir konu olması gerekmiyorsa, işleri faydasız hale getirecek yavaşlar.

Çok az bağımlılığa sahip olan ve programınızın çalışma zamanının önemli bir bölümünü kaplayan görevleri arayın.

Karşılıklı bağımlılıkları yoksa, o zaman önemsiz bir paralellik durumu söz konusudur, her birini bir iplikle kolayca kurabilir ve avantajlardan yararlanabilirsiniz.

Sınırlı karşılıklı bağımlılıkta görevler bulabilirseniz, bilgi alışverişinde bulunmak için kilitleme ve senkronizasyon onları önemli ölçüde yavaşlatmaz, o zaman çoklu okuma, senkronizasyon sırasında hatalı mantık nedeniyle kilitlenme tehlikelerinden kaçınmak için dikkatli olmanız koşuluyla biraz hız kazanabilir. gerektiğinde senkronize olmama nedeniyle hatalı sonuçlar

Alternatif olarak, çok iş parçacıklı okuma için daha yaygın olan uygulamaların bazıları (bir anlamda) önceden belirlenmiş bir algoritmanın hızlandırılmasını istemiyor, bunun yerine algoritma için daha büyük bir bütçe yazmayı planlıyorlar: bir oyun motoru yazıyorsanız ve AI'niz kare hızınıza göre bir karar vermek zorundadır, eğer kendi CPU'sunu verirseniz AI'nıza genellikle daha büyük bir CPU döngüsü bütçesi verebilirsiniz.

Bununla birlikte, dişlilerin profilini çıkardığınızdan ve bir noktada maliyeti telafi etmek için yeterli işi yaptıklarından emin olun.

Paralel Algoritmalar

Ayrıca birden fazla işlemci kullanarak hızlanabilen birçok sorun var, ancak bunlar CPU'lar arasında kolayca bölünemeyecek kadar monolitik.

Paralel algoritmalar, en iyi paralel olmayan algoritmaya göre büyük O çalışma süreleri için dikkatlice analiz edilmelidir, çünkü CPU-içi iletişim maliyetinin birden fazla CPU kullanmanın yararlarını ortadan kaldırması çok kolaydır. Genel olarak, CPU'lar arası iletişimi (büyük O terimleriyle) her CPU'da hesaplamaları kullandıklarından daha az kullanmaları gerekir.

Şu anda, kısmen, gereken karmaşık analizler nedeniyle, kısmen önemsiz paralellik oldukça yaygın olduğu için, kısmen, bilgisayarlarımızda sorun çıkaran çok fazla CPU çekirdeğine sahip olmadığımız için, kısmen büyük ölçüde akademik araştırmalar için bir alandır. Bir CPU üzerindeki makul bir zaman diliminde çözülemeyen tüm CPU'larımızı kullanarak makul bir zaman diliminde çözülebilir.


Açıkça düşünülmüş bir cevap için +1. Ancak Microsoft’un önerilerini yüz değerinden almaya özen gösterdim. .NET'in eşzamanlı ilk platform olduğunu ve bu nedenle ekosistemin eşzamanlı çözümler oluşturmayı destekleyen daha iyi tesisler / belgeler sağlama konusunda önyargılı olduğunu unutmayın. Bunun tersi, Node.js. gibi bir zaman uyumsuz ilk platformlar için geçerli olacaktır.
Evan Plaice

3

uygulama tepkisiz ve sadece yavaş ve nahoş.

Ve işte problemin. Duyarlı bir UI, performans başvurusu yapmaz. Genellikle tersi. Çalışan iş parçacığı işlerini yapmak yerine, UI girişini kontrol etmek için çok zaman harcanır.

'Sadece' bir eşzamansız yaklaşıma sahip olmakla birlikte , çoğu ortamdaki belirli bir kullanım durumu için ince ayar yapılmış olmasına rağmen , çok okuyucudur . Diğerlerinde, bu eşzamansız, her zaman eşzamanlı olmayan coroutinler aracılığıyla yapılır.

Açıkçası, zaman zaman asenkron operasyonların akıl alması ve aslında fayda sağlayacak şekilde kullanılması (performans, sağlamlık, sürdürülebilirlik) daha zor olduğunu düşünüyorum.


niye ya ? örneğin, destek sinyalleri2 kütüphanesinde bulamadığınız muzları ne buldunuz?
user1849534 15:12
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.