Kaç tane konuya sahip olmalıyım ve ne için?


81

Render ve mantık için ayrı başlıklar olmalı mı, yoksa daha da fazlası mı?

Veri senkronizasyonunun neden olduğu muazzam performans düşüşünün farkındayım (herhangi bir mutex kilitlemesine izin vermeden).

Bunu aşırı uçlara almayı ve akla gelebilecek her alt sistemi düşünüldüğünde konu açmayı düşünüyorum. Fakat bunun da yavaşlayacağı konusunda endişeliyim. (Örneğin, giriş iş parçacığını oluşturma veya oyun mantığı iş parçacığından ayırmak mantıklı mı?) Gerekli veri senkronizasyonu anlamsız mı yoksa yavaş mı?


6
hangi platform? PC, NextGen konsolu, akıllı telefonlar?
Ellis

Çok iş parçacıklı gerektirecek düşündüğüm bir şey var; ağ.
Soapy

exagerations istifa, kilitleri söz konusu olduğunda "büyük" bir yavaşlama yoktur. bu bir şehir efsanesi ve bir önyargıdır.
v.oddou

Yanıtlar:


61

Birden çok çekirdeğin avantajlarından yararlanmak için ortak yaklaşım açıkçası sadece yanlış yönlendirilmiş. Alt sistemlerinizi farklı iş parçacıklarına ayırmak işin bir kısmını birden fazla çekirdeğe bölecektir, ancak bazı büyük sorunları var. İlk olarak, çalışmak çok zor. Kim kendi yerine basit bir render veya fizik kodu yazarken, kilitlerle, senkronizasyon ve iletişim ile uğraşmak ister? İkincisi, yaklaşım aslında ölçeklenmiyor. En iyi ihtimalle, bu belki üç ya da dört çekirdekten faydalanmanıza izin verecek ve eğer gerçekten ne yaptığınızı biliyorsanız. Bir oyunda yalnızca çok sayıda alt sistem var ve bunlardan daha az CPU işleminin büyük bölümünü kaplayanlar bile var. Bildiğim birkaç iyi alternatif var.

Birincisi, her ek CPU için bir çalışan iş parçacığı ile birlikte bir ana iş parçacığı olması. Alt sistem ne olursa olsun, ana iş parçacığı, bir tür sıra (lar) aracılığıyla işçi iş parçacıklarına izole edilmiş görevler verir; Bu görevlerin kendileri de başka görevler yaratabilir. İşçi iş parçacığının tek amacı, sıradaki işlerin her seferinde sırayla alınması ve gerçekleştirilmesidir. Yine de en önemli şey, bir iş parçacığının bir görevin sonucuna ihtiyaç duyduğu anda, görev tamamlanırsa sonucun ortaya çıkabileceğidir ve eğer değilse, görevi kuyruktan güvenle kaldırabilir ve devam edip bunu yapabilir. Görevin kendisi. Yani, tüm görevler birbirine paralel olarak planlanmayacaktır. Paralel olarak yürütülebilecek işlemlerden daha fazlasına sahip olmak iyidir.Bu durumda bir şey; daha fazla çekirdek ekledikçe ölçeklendirme olasılığı olduğu anlamına gelir. Bunun bir dezavantajı, zaten sizin için bunu sağlayan bir kütüphaneye veya dil çalışma zamanına erişiminiz olmadıkça, düzgün bir sıra ve işçi döngüsü tasarlamak için çok fazla çalışma gerektirmesidir. En zor kısım, görevlerinizin gerçekten yalıtılmış ve iş parçacığınızın güvenli olduğundan emin olmanız ve görevlerinizin kaba taneli ve ince taneli arasında mutlu bir orta zeminde olduğundan emin olmaktır.

Alt sistem iş parçacıklarına başka bir alternatif, her bir alt sistemi yalıtımlı olarak paralelleştirmektir. Diğer bir deyişle, oluşturma ve fiziği kendi iş parçacıklarında çalıştırmak yerine, tüm çekirdeklerinizi bir kerede kullanmak için fizik alt sistemini, tüm çekirdeklerinizi bir seferde kullanmak için oluşturma alt sistemini yazın, ardından iki sistemin sırayla çalışmasını sağlayın (veya araya sokulmasını, oyun mimarinizin diğer yönlerine bağlı olarak). Örneğin, fizik alt sisteminde, oyundaki tüm puan kütlelerini alabilir, onları çekirdekleriniz arasında bölebilir ve ardından tüm çekirdeklerin bir kerede güncellemesini sağlayabilirsiniz. Her çekirdek, verileriniz üzerinde iyi bir konuma sahip sıkı döngülerle çalışabilir. Bu kilit adım paralellik tarzı bir GPU'nun yaptıklarına benzer. Buradaki en zor kısım, çalışmanızı ince taneli parçalara ayırdığınızdan ve eşit şekilde böldüğünüzden emin olmaktır.aslında tüm işlemciler arasında eşit miktarda çalışmayla sonuçlanır.

Bununla birlikte, bazen politikaya, mevcut yasalara veya diğer sinir bozucu durumlara bağlı olarak, her alt sisteme bir konu vermek en kolay olanıdır. Bu durumda, CPU ağır iş yükleri için çekirdeklerden daha fazla işletim sistemi iş parçacığı oluşturmaktan kaçınmak en iyisidir (yalnızca çekirdekleriniz arasında dengelenebilecek hafif ipliklerle çalışma süreniz varsa, bu büyük bir sorun değildir). Ayrıca, aşırı iletişimden kaçının. Güzel bir numara, boru hattını denemek; Her büyük alt sistem bir seferde farklı bir oyun durumu üzerinde çalışıyor olabilir. Boru hatları, aynı anda aynı verilere erişmeleri gerekmediği için alt sistemleriniz arasında gerekli iletişim miktarını azaltır ve aynı zamanda darboğazların neden olduğu zararın bir kısmını geçersiz kılabilir. Örneğin, Fizik alt sisteminizin tamamlanması uzun zaman alıyorsa ve oluşturma alt sisteminiz her zaman bekliyorsa sona ererse, görüntü alt sistemi hala bir önceki karede çalışırken bir sonraki kare için fizik alt sistemini çalıştırırsanız, mutlak kare hızınız daha yüksek olabilir çerçevesi. Aslında, eğer bu gibi sıkıntılarınız varsa ve bunları başka bir yolla kaldıramazsanız, boru tesisatı alt sistem konularıyla uğraşmanın en meşru nedeni olabilir.


"bir iş parçacığı bir görevin sonucuna ihtiyaç duyduğu anda, görev tamamlanırsa sonucu alabilir ve eğer değilse, görevi sıradan güvenle kaldırabilir ve devam edip bu görevi kendisi gerçekleştirebilir". Aynı iş parçacığının doğurduğu bir görevden mi bahsediyorsunuz? Öyleyse, o görevin kendisini oluşturan iş parçacığı tarafından bu görevin yürütülmesi daha doğru olmaz mı?
jmp97

iş parçacığı, görevi zamanlamadan bu görevi derhal yerine getirebilir.
jmp97

3
Mesele şu ki, iş parçacığının görevi paralel olarak çalıştırmanın daha iyi olup olmayacağını mutlaka bilmemelidir. Buradaki fikir, spekülatif bir şekilde nihayetinde ihtiyaç duyacağınız işi kıvılcımlandırmaktır ve eğer başka bir iplik kendini boşta bulursa, o zaman devam edebilir ve bu işi sizin için yapabilir. Eğer sonuç istediğiniz zaman bu olmazsa, görevi sıradan kendiniz de çekebilirsiniz. Bu şema, bir iş yükünü statik değil, birden çok çekirdeğe dinamik olarak dengelemek içindir.
Jake McArthur

Bu konuya geri döndüğüm için çok zaman ayırdığım için üzgünüm. Son zamanlarda gamedevlere dikkat etmiyorum. Bu muhtemelen en iyi cevap, açık ve net ve açıktır.
j riv

1
G / Ç ağır iş yükleri hakkında konuşmayı ihmal ettiğim anlamında haklısın. Bu soruya ilişkin yorumum, bunun yalnızca CPU ağır iş yükleri ile ilgili olduğu idi.
Jake McArthur,

30

Dikkate alınması gereken birkaç şey var. Alt sistem başına iş parçacığı rota düşünmek kolaydır, çünkü kod ayırma başlaması oldukça belirgindir. Ancak, alt sistemlerinizin iletişimine ne kadar ihtiyaç duyulduğuna bağlı olarak, konu içi iletişim gerçekten performansınızı kaybedebilir. Ayrıca, bu yalnızca N çekirdeğine ölçeklenir; burada N, dişlere eklediğiniz alt sistemlerin sayısıdır.

Eğer sadece mevcut bir oyunun çok okunmasını istiyorsanız, bu muhtemelen en az direnç gösteren yoldur. Ancak, birkaç oyun veya proje arasında paylaşılabilecek bazı düşük seviyeli motor sistemleri üzerinde çalışıyorsanız, başka bir yaklaşım düşünürdüm.

Biraz bükümlü olabilir, ancak işleri bir dizi iş parçacığıyla iş kuyruğu olarak ayırabilirseniz uzun vadede daha iyi ölçeklenir. En yeni ve en büyük cipsler bir gazillion çekirdeği ile ortaya çıktıkça, oyununuzun performansı da artıyor, sadece daha fazla işçi ipliğini ateşliyor.

Dolayısıyla, temel olarak, mevcut bir projeye bazı paralelliğe dayanmak istiyorsanız, alt sistemler arasında paralellik yapardım. Sıfırdan paralel ölçeklenebilirlik göz önünde bulundurularak yeni bir motor oluşturuyorsanız, bir iş sırasına bakardım.


Bahsettiğiniz sistem, Diğer James tarafından verilen cevapta belirtilen bir programlama sistemine çok benzer, bu alanda hala iyi bir ayrıntıdır;
James

3
iş kuyruğu ve işçi iş parçacığı oluşturmak için bir topluluk wiki güzel olurdu.
bot_bot 15:11

23

Bu sorunun en iyi cevabı yoktur, çünkü ne yapmaya çalıştığınıza bağlıdır.

Xbox'ın üç çekirdeği vardır ve içerik değiştirme işlemi bir sorun haline gelmeden önce birkaç ipliği işleyebilir. Bilgisayar epeyce daha fazlasıyla başa çıkabilir.

Pek çok oyun, programlama kolaylığı için tipik olarak tek bir dişliye sahiptir. Bu çoğu kişisel oyun için iyidir. Muhtemelen başka bir iş parçacığına sahip olmanız gereken tek şey, Ağ ve Ses.

Unreal'de bir oyun ipliği, iş parçacığı, ağ ipliği ve ses ipliği var (doğru hatırlıyorsam). Bu, bir çok akım-gen motoru için oldukça standart olmakla birlikte, ayrı bir işleme dişini destekleyebilmek bir ağrı olabilir ve çok fazla toprak işi gerektirir.

Rage için geliştirilen idTech5 motoru aslında herhangi bir sayıda iş parçacığı kullanıyor ve oyun görevlerini bir görev sistemi ile işlenen 'işlere' bölerek yapıyor. Açık hedefleri, ortalama oyun sistemindeki çekirdek sayısı atladığında oyun motorlarının iyi ölçeklendirilmesidir.

Kullandığım (ve yazdığım) teknoloji Ağ, Giriş, Ses, Render ve Çizelgeleme için ayrı bir konuya sahip. Daha sonra oyun görevlerini yerine getirmek için kullanılabilecek herhangi bir sayıda iş parçacığı vardır ve bu zamanlama iş parçacığı tarafından yönetilir. Bir sürü iş birbirleriyle güzel oynamak için bütün konuları alma girdi, ama çok iyi kullanım dışarı çok çekirdekli sistemler ulaştıran, iyi ve çalışıyor gibi görünüyor, bu yüzden belki de şimdilik (gerçekleştirilir görevdir; ben ses / ağ yıkmak olabilir / girdi işçiyi sadece çalışan iş parçacığının güncelleyebileceği 'görevlere' sokun).

Bu senin nihai amacına bağlı.


Genellikle Planlama sisteminin .. anılması iplik / sistem iletişimi ortalamak için iyi bir yer :) +1
James

Neden aşağı oy, aşağı oy?
jcora

12

Alt sistem başına bir iş parçacığı gitmek yanlış yoldur. Birden alt sistemlerin diğerlerinden çok daha fazlasını talep etmesi nedeniyle birden uygulamanız ölçeklenmiyor. Bu, Yüksek Komutan tarafından uygulanan iş parçacığı yaklaşımıydı ve iki çekirdeğin ötesine ölçeklemedi çünkü 16 iş parçacığı olmasına rağmen, diğer iş parçacıkları, 16 iş parçacığı olmasına rağmen, önemli miktarda CPU oluşturma ve fizik / oyun mantığı alan iki alt sisteme sahipti. sadece herhangi bir işe zorlukla ulaşıldı ve sonuç olarak oyun sadece iki çekirdeğe ölçeklendi.

Yapmanız gereken, iş parçacığı havuzu denilen bir şey kullanmak. Bu, GPU'lara uygulanan yaklaşımı yansıtır; yani, iş gönderirsiniz ve mevcut olan herhangi bir iş parçacığı basitçe gelir ve bunu yapar ve daha sonra iş parçacığı halkası tamponu gibi iş düşüncesini beklemeye geri döner. Bu yaklaşım, N çekirdek ölçekleme avantajına sahiptir ve hem düşük hem de yüksek çekirdek sayısı için ölçeklendirmede çok iyidir. Dezavantajı, bu yaklaşım için iş parçacığı sahipliğini çalışmak oldukça zordur, çünkü hangi iş parçacığının herhangi bir zamanda hangi işi yaptığını bilmek imkansızdır, bu nedenle sahiplik sorunlarını çok sıkı bir şekilde kilitlemeniz gerekir. Ayrıca, Direct3D9 gibi çoklu iş parçacıklarını desteklemeyen teknolojilerin kullanılmasını çok zorlaştırır.

İş parçacığı havuzları kullanmak çok zor, ancak mümkün olan en iyi sonuçları sunar. Çok iyi bir ölçeklendirmeye ihtiyacınız varsa veya üzerinde çalışmak için çok zamanınız varsa, bir iplik havuzu kullanın. Paralellik bilinmeyen bağımlılık sorunları ve tek iş parçacıklı teknolojileri olan mevcut bir projeye dahil etmeye çalışıyorsanız, bu sizin için bir çözüm değildir.


Sadece biraz daha kesin olmak gerekirse: GPU'lar thread havuzlarını kullanmaz, bunun yerine thread zamanlayıcı donanım içinde uygulanır; bu, thread oluşturma ve bağlam anahtarlarının pahalı olduğu CPU'ların aksine yeni thread'lar ve switch thread'leri ucuz hale getirir. Örneğin Nvidias CUDA Programcı Kılavuzuna bakın.
Nils

2
+1: Buradaki en iyi cevap. Eğer çerçeveniz izin verirse, iplik makaralarından (örneğin iş kuyrukları ve işçiler) daha fazla soyut yapı bile kullanırım. Bu terimlerle düşünmek / programlamak saf iş parçacıklarından / kilitlerden vb. Çok daha kolaydır. Artı: Oyununuzu görüntü oluşturma, mantık vb. Bölümlerine ayırmak saçmadır, çünkü görüntü oluşturma işleminin mantığın bitmesini beklemesi gerekir. Aksine, paralel olarak yürütülebilen işler oluşturun (örneğin: Bir sonraki kare için bir npc için AI'yi hesaplayın).
Dave O.

@DaveO. "Artı" puanınız çok, çok doğru.
Mühendis,

11

En kritik bölümün mümkün olduğunca senkronizasyondan kaçınmak olduğu konusunda haklısın. Bunu başarmanın birkaç yolu vardır.

  1. Verilerinizi bilin ve işleme gereksinimlerinize göre bellekte saklayın. Bu, senkronizasyona ihtiyaç duymadan paralel hesaplamalar için plan yapmanızı sağlar. Maalesef bu, çoğu zaman tahmin edilemeyen zamanlarda verilere farklı sistemlerden erişildiği için elde etmek oldukça zordur.

  2. Veriler için açık erişim zamanları tanımlayın. Ana kenenizi x fazına ayırabilirsiniz. Konu X'in verileri yalnızca belirli bir aşamada okuduğundan eminseniz, bu verilerin başka bir konudaki farklı bir aşamada değiştirilebileceğini de bilirsiniz.

  3. Verilerinizi iki kez tamponlayın. Bu en basit yaklaşımdır, ancak Konu X son kareden gelen verilerle çalışırken, Konu Y Y bir sonraki kareye veri hazırlarken gecikmeyi arttırır.

Kişisel deneyimim, ince taneli hesaplamaların en etkili yöntem olduğunu göstermektedir, çünkü bunlar alt sistem tabanlı bir çözümden çok daha iyi ölçeklenebilir. Alt sistemlerinizi bağlarsanız, çerçeve süresi en pahalı alt sisteme bağlanacaktır. Bu tüm iş parçacıklarına yol açabilir, ancak pahalı alt sistem sonunda çalışmasını bitirinceye kadar bir rölantide. Oyununuzun büyük bölümlerini küçük görevlere ayırabiliyorsanız, bu görevleri boşta kalmamak için uygun şekilde planlanabilir. Ancak, zaten büyük bir kod tabanınız varsa, başarması zor bir şey.

Bazı donanım kısıtlamalarını göz önünde bulundurmak için, donanımınızı asla abonelikten çıkarmaya çalışmalısınız. Abonelik iptali ile, platform donanım ipliklerinizden daha fazla yazılım dizisine sahip olmak anlamına gelir. Özellikle PPC mimarilerinde (Xbox360, PS3) görev anahtarı gerçekten pahalıdır. Sadece kısa bir süre için tetiklenen birkaç abonelik iptali varsa elbette tamamen sorun yok (örneğin bir kare) Bir kez PC'yi hedef alırsanız, çekirdek sayısının (veya daha iyi HW) olduğunu aklınızda bulundurmalısınız. -Threads) sürekli büyüyor, bu yüzden ek CPU gücünden faydalanan ölçeklenebilir bir çözüm bulmak istersiniz. Bu nedenle, bu alanda kodunuzu mümkün olduğunca görev tabanlı tasarlamaya çalışmalısınız.


3

Bir uygulama iş parçacığı için genel kural: CPU Çekirdeği başına 1 iş parçacığı. Bununla birlikte, 4 anlamına gelen dört çekirdekli bir PC'de belirtildiği gibi, XBox 360 bununla birlikte 3 çekirdeğe, ancak her biri 2 donanım dişine, yani bu durumda 6 dişe sahiptir. PS3 gibi bir sistemde ... bu konuda iyi şanslar :) İnsanlar hala anlamaya çalışıyorlar.

Her bir sistemi, istersen kullanabileceğin bağımsız bir modül olarak tasarlamayı öneririm. Bu genellikle, modül ile motorun geri kalanı arasında çok net bir şekilde tanımlanmış iletişim yollarına sahip olmak anlamına gelir. Özellikle Rendering ve audio gibi Salt Okunur işlemlerini ve 'henüz var mıyız' diye seslendirilecek şeyleri oynatıcı girdisini okumak gibi süreçleri seviyorum. AttackingHobo tarafından verilen cevaba dokunmak, 30-60fps yaparken, verileriniz güncel olmayan bir saniyenin 1/30/1 / 60'ındaysa, oyununuzun duyarlı hissinden uzaklaşmayacak. Uygulama yazılımı ve video oyunları arasındaki temel farkın saniyede 30-60 kez her şeyi yaptığını daima unutmayın. Ancak aynı notta,

Motorunuzun sistemlerini yeterince iyi tasarlarsanız, motorunuzu oyun başına ve benzeri şekilde daha uygun bir şekilde dengelemek için iplikten ipliğe geçirilebilir. Teoride, her bir bileşeni tamamen ayrı bilgisayar sistemlerinin çalıştığı yerde olması gerekiyorsa, motorunuzu dağıtık bir sistemde de kullanabilirsiniz.


2
Xbox360'de çekirdek başına 2 donanım
zinciri var

Ah, +1 :) Her zaman 360 ve ps3 ağ alanlarıyla sınırlı kaldım, hehe :)
James

0

Mantıksal çekirdek başına bir iş parçacığı yaratıyorum (eksi bir, Rendering'den tesadüfen sorumlu olan Ana İş parçacığını hesaba katar, ancak bunun dışında bir İşçi İş parçacığı olarak da görev yapar).

Giriş aygıtı olaylarını bir karede gerçek zamanlı olarak toplarım, ancak karenin sonuna kadar uygulamayın: sonraki karede etkili olurlar. Ve güncelleme (yeni durum) yerine oluşturma (eski durum) için benzer bir mantık kullanıyorum.

Aynı çerçevede güvenli olmayan işlemleri ertelemek için atomik olayları kullanıyorum ve işlemlerin sırası ile ilgili, kilitlemeden veya beklemeden demir kaplı bir garanti veren bir hafıza bariyeri uygulamak için birden fazla olay sırası (iş sırası) kullanıyorum. (iş öncelik sırasına göre eşzamanlı olmayan sıraları kilitleyin).

Herhangi bir işin aynı öncelik sırasına veya daha yüksek bir çerçeveye (daha sonra servis edilen) subjo'ları (daha ince ve atomikliğe yaklaşırken) verebileceğinden bahsetmek dikkat çekicidir.

Bu tür üç kuyruğum varsa, biri hariç tüm iş parçacıkları kare başına tam olarak üç kez durma potansiyeline sahip olabilir (diğer iş parçacıklarının mevcut öncelik düzeyinde yayınlanan tüm bekleyen işleri tamamlamasını beklerken).

Bu kabul edilebilir bir diş ipliği hareketsizliği seviyesi gibi görünüyor!


Benim çerçevem, önceki çerçevenin güncelleme geçişinden ÇOK DEVLENMESİ yapan MAIN ile başlıyor, diğer tüm konular NEXT çerçeve durumunu hemen hesaplamaya başlıyor, ben sadece hiç kimsenin okumadığı karede bir noktaya kadar arabellek durumu değişikliklerini iki katına çıkarmak için Events'i kullanıyorum .
Homer

0

Genelde bir ana iş parçacığı kullanırım (açıkçası) ve yüzde 10 ile 20 arasında bir performans düşüşü fark ettiğimde bir iş parçacığı ekleyeceğim. Böylesi bir düşüşü yapmak için visual studio'nun performans araçlarını kullanıyorum. Sık karşılaşılan olaylar (un) haritanın bazı alanlarını yüklemek veya bazı ağır hesaplamalar yapmaktır.

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.