Çöp toplama işleminin manuel bellek yönetiminden daha hızlı olduğunun gösterilmesi


23

Birçok yerde (heck, hatta kendim yazdım bile) çöp toplama işleminin (teorik olarak) manuel bellek yönetiminden daha hızlı olabileceğini okudum .

Bununla birlikte, göstermek, söylemekten çok daha zor.
Aslında bu etkiyi gösteren bir kod parçası görmedim .

Bu performans avantajını gösteren herhangi biri (veya nerede bulabileceğimi biliyor) koduna sahip mi?


5
GC ile sorun çoğu uygulamaları 2 çalışır çok farklı sonuçları olabilir bu yüzden deterministik olmadığını değil, o karşılaştırmak doğru değişkenleri izole etmek zordur söz
mandal ucube

@ratchetfreak: Zamanın% 70'ini ancak daha hızlı olan herhangi bir örnek biliyorsanız, benim için de sorun değil. Olmalı bazı (gecikme muhtemelen olmaz eser) en az yayılması anlamında, ikisini karşılaştırmak için bir yol.
Mehrdad,

1
Eh, bu biraz zor, çünkü GC'ye manuel olarak yaptığınız şeyin üzerinde bir kenar sağlayan her zaman el ile yapabilirsiniz. Belki de bunu "standart" manuel bellek yönetim araçları (malloc () / free (), sahip olunan işaretçiler, yeniden işaretlenmiş paylaşılan işaretçiler, zayıf işaretçiler, özel ayırıcılar yok) ile sınırlandırmak daha iyidir? Veya, özel tahsisatçılara izin verirseniz (ne tür bir programcının yaptığına bağlı olarak daha gerçekçi veya daha az gerçekçi olabilir), bu tahsisatçılara verilen çabaya kısıtlamalar getirin. Aksi takdirde, "GC'nin bu durumda ne yaptığını kopyalayın" manüel stratejisi her zaman en az GC kadar hızlı olur.

1
"GC'nin ne yaptığını" kopyalayın "demediğimde" kendi GC'nizi oluşturun "(ancak bunun bir GC için isteğe bağlı desteği sağlayan C ++ 11 ve sonrasında teorik olarak mümkün olduğunu unutmayın ). Demek istediğim, aynı yorumda daha önce ifade ettiğim gibi, "GC'ye manuel olarak yaptığınız şeyin üzerinde bir avantaj sağlar". Örneğin, Cheney benzeri bir sıkıştırma bu uygulamaya çok yardımcı oluyorsa, benzer bir ayırma + sıkıştırma şemasını manuel olarak uygulayabilir ve işaretçi düzeltmesini işlemek için özel akıllı işaretçiler kullanabilirsiniz. Ayrıca, gölge yığını gibi tekniklerle, fazladan çalışma pahasına C veya C ++ 'da kök bulma işlemini yapabilirsiniz.

1
@Ike: Sorun değil. Neden soruyu sorduğumu gördün mü? Benim sorumun amacı buydu - insanlar mantıklı olması gereken her türlü açıklamayı yaptılar ancak pratikte söylediklerinin doğru olduğunu kanıtlayan bir gösteri yapmalarını isteyince herkes tökezledi. Bu sorunun tüm amacı bir kez ve herkes için bunun gerçekte pratikte olabileceğini göstermekti.
Mehrdad,

Yanıtlar:


26

Bkz http://blogs.msdn.com/b/ricom/archive/2005/05/10/416151.aspx ve Raymond Chen vs Riko Mariani (Microsoft'ta ikisi de çok yetkin programcılar) dışarı düello görmek için tüm bağlantıları izleyin . Raymond yönetilmeyen olanı iyileştirir, Rico da aynı şeyi yönetilenlerde optimize ederek cevap verirdi.

Temelde sıfır optimizasyon çabasıyla, yönetilen sürümler kılavuzdan çok daha hızlı başlatıldı. Sonunda el kitabı yönettiği yendi, ancak çoğu programcının gitmek istemediği bir seviyeye optimize ederek. Tüm sürümlerde, el kitabının bellek kullanımı yönetilenden daha önemliydi.


C ++ yapılarının doğru kullanımı, bu kadar zor olmasa da :) koduyla gerçek bir örneği belirtmek için +1 :) swapve muhtemelen sizi oldukça kolay bir şekilde performans açısından kolaylaştıracaktır ...
Mehrdad,

5
Raymond Chen'i performanstan üstün edebilirsin. Hasta olduğum için dışarı çıkmadığı sürece yapamayacağımı, defalarca çok çalışıyorum ve şanslı olduğuma eminim. Seçtiğin çözümü neden seçmediğini bilmiyorum. Bunun sebepleri olduğundan eminim
btilly

Raymond'ın kodunu buraya kopyaladım ve karşılaştırmak için buraya kendi versiyonumu yazdım . Metin dosyasını içeren ZIP dosyası burada . Bilgisayarımda benimkiler 14 msnde, Raymond'ınlar 21 msnde çalışıyor. Ben (mümkün olan) bir şey yanlış yaptım sürece, onun 215 satırlık kod bile,% 50 daha yavaş benim 48 satırlık uygulanması daha olmadan bellek eşlemeli dosyaları veya özel bellek havuzları (o kullanımını yaptığımız) kullanarak. Benimki C # sürümünün yarısı kadardır. Yanlış mı yaptım, yoksa aynı şeyi mi gözlemledin?
Mehrdad

1
@Mehrdad Bu dizüstü bilgisayarda eski gcc kopyasını çıkartarak ne kodunuzun ne de derlemenin derlemeyeceğini bildirebilirim. Windows'da olmadığım gerçeği muhtemelen bunu açıklıyor. Ancak, numaralarınızın ve kodunuzun doğru olduğunu varsayalım. Aynı şeyi on yıllık bir derleyici ve bilgisayarda da yapıyorlar mı? (Blogun yazıldığı zamana bakın.) Belki, belki de değil. Diyelim ki onlar, (bir C programcısı olarak) C ++ 'nın nasıl doğru kullanılacağını bilmiyordu, vb.
btilly

1
Yönetilen hafızaya çevrilip hızlandırılabilen makul bir C ++ programına bırakıldık. Ancak C ++ sürümünün optimize edilip daha da hızlandırılabileceği bir yer. Hepimizin mutabık kaldığı şey, yönetilen kod yönetilmeyenden daha hızlı olduğunda her zaman gerçekleşen genel kalıptır. Bununla birlikte, yönetilen bir sürümde daha hızlı olan iyi bir programcının somut bir makul kod örneğine sahibiz.
btilly

5

Temel kural, ücretsiz öğle yemeği olmamasıdır.

GC, manuel bellek yönetiminin baş ağrısını ortadan kaldırır ve hata yapma olasılığını azaltır. Belirli bir GC stratejisinin problem için en uygun çözüm olduğu durumlar vardır, bu durumda onu kullanmak için herhangi bir ceza ödemezsiniz. Ancak başka çözümlerin daha hızlı olacağı başkaları da var. Her zaman daha yüksek soyutlamaları daha düşük bir seviyeden simüle edebildiğiniz için, ancak etrafınızdaki diğer şekilde değil, genel durumdaki daha düşük soyutlamaların daha düşük olamayacağının etkili bir şekilde olduğunu kanıtlayabilirsiniz.

GC , özel bir manuel bellek yönetimi durumudur

Manuel olarak daha iyi performans elde etmek için çok fazla iş ya da daha fazla hata olabilir, ancak bu farklı bir hikaye.


1
Bu bana hiç mantıklı gelmiyor. Size birkaç somut örnek vermek gerekirse: 1) GC'lerin üretimindeki tahsisatçılar ve yazma engelleri elle yazılmış bir birleştiricidir, çünkü C çok verimsizdir, böylece bunu nasıl yeneceksin? C derleyicisi tarafından yapılmayan ve bu nedenle C'de yapılamayan yüksek seviyeli (işlevsel) dillerde yapılır. Yığın yürüme, yüksek seviyedeki diller tarafından C seviyesinin altında yapılan bir başka örnektir.
Jon Harrop,

2
1) Yorum yapmak için belirli bir kodu görmem gerekirdi, ancak assembler içindeki tahsis ediciler / engeller yazılan el daha hızlı ise, elle yazılmış assembler kullanın. Bunun GC ile ne alakası olduğundan emin değilim. 2) Burada bir göz atın: stackoverflow.com/a/9814654/441099 , bazı GC olmayan dilin sizin için kuyruk özyineleme eleme işlemi yapıp yapamayacağı değil. Mesele şu ki kodunuzu hızlı ya da daha hızlı olacak şekilde dönüştürebilirsiniz. Belirli bir dilin derleyicisinin bunu sizin için otomatik olarak yapıp yapamayacağı bir kolaylık meselesidir. Yeterince düşük bir soyutlamada, dilerseniz bunu her zaman kendiniz yapabilirsiniz.
Guy Sirton,

1
C'deki bu kuyruk çağrısı örneği yalnızca kendisini arayan bir işlevin özel durumu için işe yarar. C kuyruk çağıran genel fonksiyon durumlarını idare edemiyor. Birleştiriciye düşmek ve gelişme için sonsuz zaman olduğunu varsaymak bir Turing tarpitidir.
Jon Harrop,

3

GC'nin manuel yöntemlerden çok daha verimli olduğu yapay bir durum oluşturmak kolaydır - sadece çöp toplayıcı için tek bir "kök" olduğunu ve her şeyin çöp olduğunu ayarlayın, böylece GC adımı anında tamamlanır.

Eğer düşünürseniz, bu, bir işleme ayrılan hafızayı toplarken kullanılan modeldir. Süreç öldü, hafızası tamamen çöp, biz bittik. Pratik açıdan bile, hiçbir iz bırakmadan başlayan, koşan ve ölen bir süreç, sonsuza dek başlatan ve devam eden bir süreçten daha verimli olabilir.

Atık toplama dili olan dillerde yazılmış pratik programlar için, atık toplama işleminin avantajı hız değil, doğruluk ve basitliktir.


Yapay bir örnek oluşturmak kolaysa, basit bir örnek gösterebilir misiniz?
Mehrdad,

1
@Mehrdad Basit bir tane açıkladı. Çıkmadan önce GC versiyonunun çöp atma işlemini gerçekleştiremediği bir program yazın. Manuel bellek tarafından yönetilen sürüm daha yavaş olacaktır çünkü açıkça bir şeyleri izliyor ve serbest bırakıyordu.
btilly

3
@btilly: "GC sürümünün çıkmadan önce bir çöp çalışması yapamadığı bir program yazın." ... ilk etapta çöp toplama yapmamak, çalışan bir GC'nin bulunmamasından kaynaklanan bir bellek sızıntısıdır; GC'nin varlığından dolayı performans artışı olmaz ! Bu abort()program çıkmadan önce C ++ 'ı çağırmak gibi . Anlamsız bir karşılaştırma; çöp toplama bile değilsin, sadece bellek sızıntısına izin veriyorsun. Çöp toplama işleminin daha hızlı (veya daha yavaş) olduğunu söyleyemezsiniz, eğer çöp toplamaya başlamazsanız ...
Mehrdad

Aşırı bir örnek yapmak için, kendi öbek ve öbek yönetiminizle komple bir sistem tanımlamanız gerekir, ki bu harika bir öğrenci projesi olurdu, ancak bu marj için sığmayacak kadar büyük. Gc olmayan bellek yönetimi yöntemlerine stresli olacak şekilde tasarlanmış rasgele boyutlu dizileri tahsis eden ve bunları kaldıran bir program yazarak oldukça iyi iş çıkarırsınız.
Ddyer

3
@Mehrdad Öyle değil. Senaryo, GC versiyonunun farklı bir veri setinde doğru şekilde performans göstermemesi değil, bir çalışma yapmış olduğu eşiğe çarpmadığıdır. Bu GC sürümü için önemsiz derecede iyi olacak, ancak nihai performansın iyi bir göstergesi değil.
btilly

2

GC'nin sadece bir bellek yönetimi stratejisi olmadığı düşünülmelidir; Ayrıca, dilin ve çalışma zamanı ortamının tüm tasarımını talep eder, bu da masrafları (ve faydaları) getirir. Örneğin, GC'yi destekleyen bir dil, işaretçilerin çöp toplayıcıdan gizlenemediği ve genellikle dikkatlice yönetilen sistem ilkeleri dışında inşa edilemeyecekleri bir formda derlenmelidir. Diğer bir husus, GC'nin tamamlanmasına izin verilmesi gereken bazı adımları dayattığı için yanıt süresi garantilerini sürdürmenin zorluğudur.

Sonuç olarak, eğer çöp toplanan bir dile sahipseniz ve aynı sistemdeki manuel olarak yönetilen bellekle hızı karşılaştırırsanız, kullanmıyor olsanız bile çöp toplanmasını desteklemek için ek yükü ödemek zorundasınız.


2

Daha hızlı şüpheli. Ancak, donanım destekliyorsa ultra hızlı, algılanamaz veya daha hızlı olabilir. Uzun zaman önce LISP makineleri için böyle tasarımlar vardı. Biri GC'yi donanımın bellek alt sistemine yerleştirdi, öyle ki ana CPU orada olduğunu bilmiyordu. Daha sonraki birçok tasarımda olduğu gibi, GC ana işlemciyle eşzamanlı olarak çok az duraklama yapmasına gerek kalmadan çalıştı. Daha modern bir tasarım, Java kodunu JVM'nin amaca yönelik işlemciler ve duraksız bir GC kullanarak daha hızlı çalıştıran Azul Systems Vega 3 makineleri. Google’ın (veya Java’nın) ne kadar hızlı olduğunu bilmek istiyorsanız Google’ı kullanın.


2

Bu konuda oldukça çalıştım ve bazılarını burada açıkladım . Boamm GC'yi C ++ ile kıyaslamıştım, kullanarak tahsis ettim mallocama serbest bırakma, tahsis etme ve serbest bırakma freeve C + 'da yazılmış özel olarak oluşturulmuş bir işaretleme bölgesi GC'sini listeledi, hepsi OCaml'in GC-listesini kullanarak liste tabanlı bir n-kraliçe çözücü kullandı. OCaml’ın GC’si her durumda daha hızlıydı. C ++ ve OCaml programları kasıtlı olarak aynı tahsisleri aynı sıraya göre yapmak için yazılmıştır.

Elbette, problemi çözmek için programları yalnızca 64-bit tam sayılarla ve ayırmalar olmadan yeniden yazabilirsiniz. Her ne kadar daha hızlı olsa da, bu alıştırmanın amacını yenecektir (ki bu yeni bir GC algoritmasının performansını öngörecekti. C ++ 'da yerleşik bir prototip kullanarak çalışıyordum).

Sektörde yıllardır gerçek C ++ kodunu yönetilen dillere taşımakla geçirdim. Neredeyse her vakada, çoğu muhtemelen GC trumping manuel bellek yönetiminden dolayı önemli performans iyileştirmeleri gözlemledim. Pratik sınırlama, bir mikro Benchmark'ta neler yapılabileceği değil, bir son teslim tarihinden önce neler yapılabileceği ve GC-merkezli diller, asla geriye bakmadığım kadar büyük verimlilik geliştirmeleri sunuyor. Hala gömülü cihazlarda (mikrodenetleyiciler) C ve C ++ kullanıyorum ama bu şimdi bile değişiyor.


+1 teşekkürler. Karşılaştırma kodunu nerede görebilir ve çalıştırabiliriz?
Mehrdad

Kod yer hakkında dağınık. Buraya mark-region versiyonunu gönderdim: groups.google.com/d/msg/…
Jon Harrop

1
Burada hem ipliğin hem güvenli hem de güvensiz olduğu sonuçları vardır.
Jon Harrop

1
@Mehrdad: "Bu tür olası hata kaynaklarını ortadan kaldırdınız mı?". Evet. OCaml kaçış analizi gibi bir optimizasyon olmadan çok basit bir derleme modeline sahiptir. OCaml'in kapatmayı göstermesi, aslında C ++ çözümünden oldukça yavaştır, bu nedenle List.filterC ++ 'ın yaptığı gibi bir gelenek kullanmalıdır . Ancak, evet, kesinlikle bazı RC işlemlerinin seçilebilmesi konusunda haklısınız. Ancak, vahşi doğada gördüğüm en büyük sorun, insanların bu tür optimizasyonları büyük endüstriyel kod tabanlarında elle yapmak için zamanlarının olmamasıdır.
Jon Harrop,

2
Evet kesinlikle. Yazma ek bir çaba yok ama kod yazma C ++ ile tıkanıklık değildir. Kod korunuyor. Bu tür rastlantısal karmaşıklıkla kod korumak bir kabustur. Çoğu endüstriyel kod tabanı milyonlarca kod satırıdır. Sadece bununla uğraşmak istemezsin. İnsanların her şeyi shared_ptrsadece eşzamanlılık hatalarını düzeltmek için dönüştürdüğünü gördüm . Kod çok daha yavaş ama, hey, şimdi çalışıyor.
Jon Harrop,

-1

Böyle bir örnek, mutlaka kötü bir manuel bellek ayırma şemasına sahiptir.

En iyi çöp toplayıcıyı kabul edin GC. Dahili olarak bellek ayırma yöntemleri, hangi belleğin serbest bırakılabileceğini belirleme yöntemleri ve sonunda serbest bırakma yöntemleri vardır. Birlikte bunlar hepsinden daha az zaman alır GC; Diğer yöntemlerde biraz zaman harcanır GC.

Şimdi, aynı tahsis mekanizmasını kullanan GCve free()çağrıları aynı yöntemle serbest bırakılacak hafızayı bir kenara koyan manuel bir tahsisatçı düşünün GC. Tarama aşaması yoktur, başka yöntemlerden hiçbirine sahip değildir. Bu mutlaka daha az zaman alır.


2
Bir çöp toplayıcı genellikle her bir nesneden sonra belleği yararlı bir duruma getirmek zorunda kalmadan çoğu nesneyi serbest bırakabilir. Dizi listesinden belirli bir kritere uyan tüm öğeleri kaldırma görevini düşünün. Bir N maddesi listesinden tek bir öğenin çıkarılması O (N); N listesinden M öğelerinin çıkarılması, bir kerede bir O (M * N) 'dir. Bununla birlikte, listedeki tek bir geçişte bir kriteri karşılayan tüm öğeleri kaldırmak O (1).
supercat

@supercat: freepartileri de toplayabilir. (Tabii ki bir kriteri karşılayan tüm maddeleri kaldırmak hala O (N) 'dir, eğer sadece listenin kendisinden
geçerse

Bir kriteri karşılayan tüm öğeleri kaldırmak en az O (N) 'dir. freeHer bellek öğesinin kendisiyle ilişkilendirilmiş bir bayrağı varsa, toplu toplama modunda çalışabilecek şekilde haklısınız , ancak GC hala bazı durumlarda öne çıkabiliyor. Birinde bir N nesneden L farklı maddeleri tanımlayan M referansları varsa, hiçbir referansın bulunmadığı ve kalanı birleştiren her referansı kaldırma zamanı, O (N) yerine O (M) olur. Birinde M fazla boş alan varsa, ölçeklendirme sabiti oldukça küçük olabilir. Dahası, taramalı olmayan bir GC sisteminde
kompaktlaştırma için gerekenler

@supercat: Evet, kesinlikle ilk yorumlarda son cümleniz O (1) değil.
MSalters

1
@ MSalters: "Ve deterministik bir planın çocuk odası olmasını engelleyen şey nedir?". Hiçbir şey değil. OCaml'ın izini süren çöp toplayıcı belirleyicidir ve bir kreş kullanır. Fakat bu “manuel” değildir ve “deterministik” kelimesini kötüye kullandığınızı düşünüyorum.
Jon Harrop
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.