"CPU bağlı" ve "I / O bağlı" terimleri ne anlama geliyor?
"CPU bağlı" ve "I / O bağlı" terimleri ne anlama geliyor?
Yanıtlar:
Oldukça sezgisel:
Bir program, CPU daha hızlı olsaydı daha hızlı giderse CPU'ya bağlıdır, yani zamanının çoğunu sadece CPU kullanarak harcıyor (hesaplamalar yapıyor). Yeni its rakamlarını hesaplayan bir program tipik olarak CPU'ya bağlı olacaktır, sadece sayıları eziyor.
Bir program, G / Ç alt sistemi daha hızlı olsaydı daha hızlı giderse G / Ç'ye bağlıdır. Hangi kesin G / Ç sisteminin kastedildiği değişebilir; Genellikle diskle ilişkilendiririm, ancak elbette ağ veya genel olarak iletişim de yaygındır. Bazı veriler için büyük bir dosyaya bakan bir program G / Ç'ye bağlı olabilir, çünkü darboğaz o zaman diskten verilerin okunmasıdır (aslında, bu örnek belki de bugünlerde yüzlerce MB / s ile eski moda bir çeşittir) SSD'lerden geliyor).
CPU Bound , işlemin ilerleme hızı CPU hızı ile sınırlı olduğu anlamına gelir. Küçük sayı kümelerinde hesaplamalar yapan, örneğin küçük matrisleri çarpma gibi bir görevin CPU'ya bağlı olması muhtemeldir.
G / Ç Sınırı , bir işlemin ilerleme hızı G / Ç alt sisteminin hızı ile sınırlı olduğu anlamına gelir. Örneğin, bir dosyadaki satır sayısını saymak gibi verileri diskten işleyen bir görevin G / Ç'ye bağlı olması muhtemeldir.
Bellek bağlı , bir işlemin ilerleme hızı, kullanılabilir bellek miktarı ve bu belleğe erişim hızıyla sınırlıdır. Büyük miktarda bellek verisi işleyen, örneğin büyük matrisleri çarpma gibi bir görevin büyük olasılıkla Bellek Bağlı olması muhtemeldir.
Önbellek bağlı , bir işlem ilerlemesinin kullanılabilir önbellek miktarı ve hızı ile sınırlı olduğu anlamına gelir. Önbelleğe sığmayacak kadar çok veri işleyen bir görev önbelleğe bağlı olacaktır.
G / Ç Bağlama, Bellek Bağlama'dan Önbellek Bağlama işlemcisinden CPU Bağlantısı'na göre daha yavaş olacaktır.
G / Ç bağlı olmanın çözümü, daha fazla Bellek elde etmek zorunda değildir. Bazı durumlarda, erişim algoritması G / Ç, Bellek veya Önbellek sınırlamaları etrafında tasarlanabilir. Bkz. Önbellek Oblivious Algoritmaları .
Multi-Threading
Bu cevapta, CPU ile IO sınırlı iş arasında ayrım yapmak için önemli bir kullanım durumunu araştıracağım: çok iş parçacıklı kod yazarken.
RAM G / Ç bağlantılı örnek: Vektör Toplamı
Tek bir vektörün tüm değerlerini toplayan bir program düşünün:
#define SIZE 1000000000
unsigned int is[SIZE];
unsigned int sum = 0;
size_t i = 0;
for (i = 0; i < SIZE; i++)
/* Each one of those requires a RAM access! */
sum += is[i]
Her bir çekirdeğiniz için diziyi eşit olarak bölerek, ortak modern masaüstlerinde sınırlı kullanışlılığa paraleldir.
Örneğin, Ubuntu 19.04, CPU'lu Lenovo ThinkPad P51 dizüstü bilgisayarımda: Intel Core i7-7820HQ CPU (4 çekirdek / 8 konu), RAM: 2x Samsung M471A2K43BB1-CRC (2x 16GiB) Şunun gibi sonuçlar alıyorum:
Ancak çalışma arasında çok fazla varyans olduğunu unutmayın. Ama zaten 8GiB'de olduğum için dizi boyutunu daha fazla artıramıyorum ve bugün birden fazla çalışmadaki istatistikler için havamda değilim. Ancak bu, birçok manuel çalışma yaptıktan sonra tipik bir çalışma gibi görünüyordu.
Karşılaştırma kodu:
pthread
Grafikte kullanılan POSIX C kaynak kodu .
Ve burada benzer sonuçlar üreten bir C ++ sürümü .
Eğrinin şeklini tam olarak açıklayacak kadar bilgisayar mimarisi bilmiyorum, ama bir şey açık: hesaplama, 8 parçamın hepsini kullandığım için safça beklendiği gibi 8 kat daha hızlı hale gelmiyor! Bazı nedenlerden dolayı, 2 ve 3 iş parçacıkları en uygun çözümdü ve daha fazlasını eklemek işleri daha yavaş hale getiriyor.
Bunu CPU'ya bağlı çalışma ile karşılaştırın, ki bu aslında 8 kat daha hızlıdır: 'Gerçek', 'kullanıcı' ve 'sys' zaman çıktısında ne anlama gelir (1)?
Tüm işlemcilerin RAM'e bağlanan tek bir bellek veri yolunu paylaşmasının nedeni:
CPU 1 --\ Bus +-----+
CPU 2 ---\__________| RAM |
... ---/ +-----+
CPU N --/
böylece bellek veri yolu hızla CPU değil darboğaz haline gelir.
Bunun nedeni, iki sayı eklemenin tek bir CPU döngüsü gerektirmesi, bellek okumalarının 2016 donanımında yaklaşık 100 CPU döngüsü sürmesidir.
Bu nedenle, giriş verisi baytı başına yapılan CPU çalışması çok küçüktür ve buna IO'ya bağlı bir süreç diyoruz.
Bu hesaplamayı daha da hızlandırmanın tek yolu, bireysel bellek erişimini yeni bellek donanımıyla, örneğin Çok kanallı bellekle hızlandırmak olacaktır .
Örneğin daha hızlı bir CPU saatine yükseltmek çok yararlı olmaz.
Diğer örnekler
matris çarpımı RAM ve GPU'larda CPU'ya bağlıdır. Giriş şunları içerir:
2 * N**2
sayılar, ancak:
N ** 3
çarpmalar yapılır ve bu paralel büyüklükteki pratik N için buna değecek kadar yeterlidir.
Bu nedenle aşağıdaki gibi paralel CPU matris çarpma kitaplıkları bulunmaktadır:
Önbellek kullanımı, uygulamaların hızı üzerinde büyük bir fark yaratır. Örneğin, bu didaktik GPU karşılaştırma örneğine bakın .
Ayrıca bakınız:
Ağ oluşturma, prototip IO'ya bağlı örnektir.
Tek bir bayt veri gönderdiğimizde bile, hedefine ulaşması hala çok zaman alır.
HTTP istekleri gibi küçük ağ isteklerini paralel hale getirmek büyük bir performans kazancı sağlayabilir.
Ağ zaten tam kapasiteye sahipse (örn. Torrent indirmek), paralelleştirme yine de gecikmeyi artırabilir (örneğin, bir web sayfasını "aynı anda" yükleyebilirsiniz).
Bir sayı alan ve çok fazla crunches kukla C ++ CPU bağlı işlem:
Sıralama aşağıdaki denemeye göre CPU gibi görünüyor: C ++ 17 Paralel Algoritmalar zaten uygulanmış mı? Paralel sıralama için 4 kat performans artışı gösterdi ancak daha teorik bir onay almak istiyorum.
CPU veya IO bağlı olup olmadığınızı nasıl öğrenebilirsiniz?
RAM olmayan IO, disk, ağ: gibi bağlı ps aux
, eğer CPU% / 100 < n threads
. Evet ise, IO'ya bağlısınız, örneğin engelleme read
s sadece veri bekliyor ve programlayıcı bu işlemi atlıyor. Ardından sudo iotop
, hangi IO'nun sorun olduğuna tam olarak karar vermek gibi diğer araçları kullanın .
Ya da, yürütme hızlıysa ve iş parçacıklarının sayısını parametreleştirirseniz, time
CPU'ya bağlı iş için iş parçacığı sayısı arttıkça bunu iş performansının arttığından kolayca görebilirsiniz : 'Gerçek', 'kullanıcı' ve 'sys' ne anlama gelir? zamanın çıktısı (1)?
RAM-IO bağlı: söylemesi daha zor, RAM bekleme süresi CPU%
ölçümlere dahil olduğundan , ayrıca bkz:
Bazı seçenekler:
GPU'lar
Giriş verilerini normal CPU tarafından okunabilen RAM'den GPU'ya ilk aktardığınızda GPU'larda bir GÇ darboğazı vardır.
Bu nedenle, GPU'lar yalnızca CPU bağlantılı uygulamalar için CPU'lardan daha iyi olabilir.
Ancak veriler GPU'ya aktarıldıktan sonra, bu baytlarda CPU'nun yapabileceğinden daha hızlı çalışabilir, çünkü GPU:
çoğu CPU sisteminden daha fazla veri yerelleştirmesine sahiptir ve bu nedenle bazı çekirdekler için verilere diğerlerinden daha hızlı erişilebilir
hemen kullanılmaya hazır olmayan herhangi bir verinin üzerinden atlayarak veri paralelliğinden yararlanır ve gecikmeyi feda eder.
GPU'nun büyük paralel giriş verileri üzerinde çalışması gerektiğinden, geçerli verilerin kullanılabilir olmasını beklemek ve CPU'nun çoğunlukla yaptığı gibi diğer tüm işlemleri engellemek yerine, mevcut olabilecek bir sonraki veriye atlamak daha iyidir.
Bu nedenle, uygulamanız varsa GPU bir CPU'dan daha hızlı olabilir:
Bu tasarım seçenekleri, asıl adımları OpenGL'deki gölgelendiriciler nelerdir ve bunlara ne için ihtiyacımız var?
ve böylece bu uygulamaların CPU'ya bağlı olduğu sonucuna varıyoruz.
Programlanabilir GPGPU'nun ortaya çıkmasıyla, CPU'ya bağlı işlemlere örnek olarak hizmet veren birkaç GPGPU uygulamasını gözlemleyebiliriz:
GLSL gölgelendiricilerle görüntü işleme?
Bulanıklaştırma filtresi gibi yerel görüntü işleme işlemleri doğada oldukça paraleldir.
Nokta verilerinden saniyede 60 kez bir ısı haritası oluşturmak mümkün müdür?
Eğer çizilen fonksiyon yeterince karmaşıksa, ısı haritası grafiklerinin çizilmesi.
Jesús Martín Berlanga tarafından https://www.youtube.com/watch?v=fE0P6H8eK4I "Gerçek Zamanlı Akışkanlar Dinamiği: CPU ve GPU"
Akışkanlar dinamiğinin Navier Stokes denklemi gibi kısmi diferansiyel denklemlerin çözümü :
Ayrıca bakınız:
CPython Global Yorumlayıcı Kilidi (GIL)
Hızlı bir vaka çalışması olarak, Python Global Tercüman Kilidi'ne (GIL) dikkat çekmek istiyorum: CPython'daki küresel tercüman kilidi (GIL) nedir?
Bu CPython uygulama ayrıntısı, birden çok Python iş parçacığının CPU'ya bağlı işleri verimli bir şekilde kullanmasını engeller. CPython dokümanlar ki:
CPython uygulama ayrıntısı: CPython'da, Global Tercüman Kilidi nedeniyle, Python kodunu aynı anda yalnızca bir iş parçacığı yürütebilir (belirli performans odaklı kitaplıklar bu sınırlamanın üstesinden gelebilir). Uygulamanızın çok çekirdekli makinelerin hesaplama kaynaklarını daha iyi kullanmasını istiyorsanız
multiprocessing
veya seçeneğini kullanmanız önerilirconcurrent.futures.ProcessPoolExecutor
. Bununla birlikte, aynı anda birden fazla I / O bağlantılı görevi çalıştırmak istiyorsanız, iş parçacığı oluşturma yine de uygun bir modeldir.
Bu nedenle, burada CPU'ya bağlı içeriğin uygun olmadığı ve G / Ç sınırının uygun olduğu bir örneğimiz var.
CPU bağlı, programın CPU veya merkezi işlem birimi tarafından tıkanmış olduğu anlamına gelirken, I / O bağlı programın G / Ç tarafından veya darbelere, ağa okuma veya yazma gibi giriş / çıkışla tıkanmış olduğu anlamına gelir.
Genel olarak, bilgisayar programlarını optimize ederken, darboğazı araştırmaya ve ortadan kaldırmaya çalışır. Programınızın CPU'ya bağlı olduğunu bilmek, birinin başka bir şeyi gereksiz yere optimize etmemesine yardımcı olur.
[Ve “darboğaz” ile, programınızı aksi takdirde olduğundan daha yavaş yapan şey demek istiyorum.]
Aynı fikri ifade etmenin başka bir yolu:
CPU'yu hızlandırmak programınızı hızlandırmazsa, G / Ç'ye bağlı olabilir.
G / Ç'yi hızlandırmak (örneğin daha hızlı bir disk kullanmak) yardımcı olmazsa, programınız CPU'ya bağlı olabilir.
("Olabilir" i kullandım çünkü diğer kaynakları dikkate almanız gerekiyor. Bellek bir örnektir.)
Programınız G / Ç'yi beklerken (örn. Bir disk okuma / yazma veya ağ okuma / yazma vb.), CPU, programınız durdurulmuş olsa bile diğer görevleri yapmakta serbesttir. Programınızın hızı çoğunlukla G / Ç'nin ne kadar hızlı olabileceğine bağlı olacaktır ve hızlandırmak isterseniz G / Ç'yi hızlandırmanız gerekecektir.
Programınız çok sayıda program talimatı çalıştırıyorsa ve G / Ç'yi beklemiyorsa, CPU'ya bağlı olduğu söylenir. CPU'nun hızlandırılması, programın daha hızlı çalışmasını sağlayacaktır.
Her iki durumda da, programı hızlandırmanın anahtarı donanımı hızlandırmak değil, programı ihtiyaç duyduğu IO veya CPU miktarını azaltmak için optimize etmek veya CPU yoğun olsa da I / O yapmasını sağlamak olabilir. şey.
G / Ç sınırı, bir hesaplamayı tamamlamak için geçen sürenin temel olarak giriş / çıkış işlemlerinin tamamlanmasını beklemek için harcanan süre ile belirlendiği bir koşulu belirtir.
Bu, CPU'ya bağlı bir görevin tersidir. Bu durum, verilerin talep edilme oranı, tüketilen orandan daha yavaş olduğunda veya başka bir deyişle, veri istemekten daha çok zaman harcamakla ortaya çıkar.
Microsoft'un söylediklerine bakın.
Eşzamansız programlamanın özü, eşzamansız işlemleri modelleyen Görev ve Görev nesneleridir. Bunlar zaman uyumsuz tarafından desteklenir ve anahtar kelimeler beklenir. Model çoğu durumda oldukça basittir:
G / Ç bağlantılı kod için, zaman uyumsuz bir yöntemin içinde bir Görev veya Görev döndüren bir işlem beklersiniz.
CPU'ya bağlı kod için, arka plan iş parçacığında Task.Run yöntemiyle başlatılan bir işlem beklersiniz.
Await anahtar kelimesi büyünün gerçekleştiği yerdir. Beklenen yöntemi arayan kişinin kontrolünü sağlar ve sonuçta bir kullanıcı arayüzünün duyarlı olmasına veya bir servisin elastik olmasına izin verir.
G / Ç Bağlama Örneği: Bir web hizmetinden veri indirme
private readonly HttpClient _httpClient = new HttpClient();
downloadButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI as the request
// from the web service is happening.
//
// The UI thread is now free to perform other work.
var stringData = await _httpClient.GetStringAsync(URL);
DoSomethingWithData(stringData);
};
CPU'ya Bağlı Örnek: Bir Oyun için Hesaplama Yapma
private DamageResult CalculateDamageDone()
{
// Code omitted:
//
// Does an expensive calculation and returns
// the result of that calculation.
}
calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone()
// performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};
Yukarıdaki örnekler, I / O bağlantılı ve CPU bağlantılı işleri nasıl zaman uyumsuz kullanabileceğinizi ve bekleyebileceğinizi göstermiştir. Yapmanız gereken bir işin G / Ç veya CPU'ya bağlı olduğunu belirleyebileceğiniz anahtardır, çünkü kodunuzun performansını büyük ölçüde etkileyebilir ve belirli yapıların yanlış kullanılmasına neden olabilir.
Herhangi bir kod yazmadan önce sormanız gereken iki soru:
Kodunuz veritabanındaki veriler gibi bir şeyi "bekliyor" olacak mı?
- Cevabınız "evet" ise, çalışmanız I / O sınırlıdır.
Kodunuz çok pahalı bir hesaplama mı yapacak?
- "Evet" yanıtı verdiyseniz, çalışmanız CPU'ya bağlıdır.
Yaptığınız iş I / O bağlıysa, zaman uyumsuz kullanın ve Task.Run olmadan bekleyin . Görev Paralel Kitaplığı'nı kullanmamalısınız. Bunun nedeni Async in Depth (Derinlik makalesinde) makalesinde ana hatlarıyla verilmiştir .
Yaptığınız iş CPU'ya bağlıysa ve yanıt vermeyi önemsiyorsanız, zaman uyumsuzluğu kullanın ve bekleyin ancak işi Task.Run ile başka bir iş parçacığında ortaya çıkarın. Çalışma eşzamanlılık ve paralellik için uygunsa, Görev Paralel Kütüphanesini de kullanmayı düşünmelisiniz .
Uygulama sırasında aritmetik / mantıksal / kayan nokta (A / L / FP) performansı çoğunlukla işlemcinin teorik tepe performansına yakın olduğunda (üretici tarafından sağlanan ve işlemci: çekirdek sayısı, frekans, kayıtlar, ALU'lar, FPU'lar vb.).
Peek performansının imkansız demediği için gerçek dünya uygulamalarında elde edilmesi çok zordur. Uygulamaların çoğu yürütmenin farklı bölümlerindeki belleğe erişir ve işlemci birkaç döngü sırasında A / L / FP işlemleri yapmaz. Bellek ve işlemci arasındaki mesafe nedeniyle buna Von Neumann Sınırlaması denir .
CPU tepe performansına yakın olmak istiyorsanız, ana bellekten veri gerektirmemesi için önbellek içindeki verilerin çoğunu yeniden kullanmaya çalışmak bir strateji olabilir. Bu özellikten yararlanan bir algoritma matris-matris çarpımıdır (eğer her iki matris de önbellekte saklanabilirse). Bunun nedeni, matrislerin boyutu büyükse, yalnızca FP sayıları kullanan işlemler n x n
hakkında yapmanız gerektiğidir . Diğer yandan, örneğin, sadece aynı verilere sahip FLOP'ları gerektirdiğinden, matris eklemesi , matris çarpımından daha az CPU ya da daha fazla belleğe bağlı bir uygulamadır .2 n^3
2 n^2
n^2
Aşağıdaki şekilde, bir Intel i5-9300H'de matris ilavesi ve matris çarpımı için naif bir algoritma ile elde edilen FLOP'lar gösterilmektedir:
Beklendiği gibi matris çarpımının performansının matris ilavesinden daha büyük olduğunu unutmayın. Bu sonuçlar çalıştırılarak çoğaltılabilir test/gemm
ve test/matadd
bu depoda bulunabilir .
Bu etki hakkında J. Dongarra tarafından verilen videoyu da izlemenizi öneririm .
G / Ç Bağlı işlemi: - Bir işlemin ömrünün büyük bir kısmı i / o durumunda harcanırsa, işlem a / o bağlı işlemdir. Örnek: -hesaplayıcı, internet explorer
CPU Bound işlemi: - İşlem ömrünün büyük bir kısmı cpu'da harcanırsa, cpu bağlantılı bir süreçtir.