STL büyük uygulamalarda kullanılmamalıdır?


24

Bu garip bir soru gibi gelebilir, ancak bölümümde aşağıdaki durumla ilgili sorun yaşıyoruz:

Burada, daha büyük ve daha da büyüyen bir sunucu uygulaması üzerinde çalışıyoruz, hatta farklı parçalara bölmeyi düşündüğümüz noktada (DLL dosyaları), gerektiğinde dinamik olarak yükleme ve daha sonra boşaltma işlemleri yapabilmek için performans sorunları.

Ancak, kullandığımız fonksiyonlar, giriş ve çıkış parametrelerini STL nesneleri olarak geçiyor ve Yığın Taşması cevabında da belirtildiği gibi , bu çok kötü bir fikir. (Yazı bazı ± çözümler ve kesmeler içeriyor, ancak hepsi çok sağlam görünmüyor.)

Açıkçası giriş / çıkış parametrelerini standart C ++ tipleriyle değiştirebilir ve bir kez fonksiyonların içindekilerden STL nesneleri yaratabiliriz, ancak bu performans düşmelerine neden olabilir.

Tek bir bilgisayarın artık kaldıramayacağı kadar büyük bir uygulama geliştirmeyi planlıyorsanız, STL'yi hiç bir teknoloji olarak kullanmamanız gerektiğine karar vermek doğru mudur?

Bu soru hakkında daha fazla bilgi:
Soruyla ilgili bazı yanlış anlamalar var gibi görünüyor: sorun şu:
Uygulamam çalışmasını tamamlamak için büyük miktarda performans (CPU, bellek) kullanıyor ve bu çalışmayı bölmek istiyorum farklı parçalara (program zaten çoklu fonksiyonlara bölündüğü için), uygulamalarımdan bazı DLL'ler oluşturmak ve bu işlevlerin bazılarını bu DLL'lerin dışa aktarma tablosuna koymak zor değildir. Bu, aşağıdaki durumla sonuçlanacaktır:

+-----------+-----------+----
| Machine1  | Machine2  | ...
| App_Inst1 | App_Inst2 | ...
|           |           |    
| DLL1.1    | DLL2.1    | ...
| DLL1.2    | DLL2.2    | ...
| DLL1.x    | DLL2.x    | ...
+-----------+-----------+----

App_Inst1, Machine1'de yüklü olan uygulamanın bir örneğidir; App_Inst2, Machine2'ye yüklenen aynı uygulamanın bir örneğidir.
DLL1.x, Machine1'de yüklü bir DLL iken, DLL2.x, Machine2'de yüklü bir DLL'dir.
DLLx.1 verilen işlevi1 kapsar.
DLLx.2 verilen işlevi2 kapsar.

Şimdi Makine1'de, işlev1 ve işlev2'yi çalıştırmak istiyorum. Bunun Machine1'i aşırı yükleyeceğini biliyorum, bu nedenle App_Inst2'ye bir mesaj göndermek istiyorum, bu uygulama örneğinden function2 işlemini yapmasını istiyorum.

Function1 ve function2'nin girdi / çıktı parametreleri STL (C ++ Standart Tür Kitaplığı) nesneleridir ve müşteriden App_Inst1, App_Inst2, DLLx.y güncellemelerini yapmasını bekleyebilirim (ancak hepsi değil, müşteri Machine1'i yükseltebilir, ancak Machine2 değil, veya yalnızca uygulamaları yükseltin, ancak dll'leri veya tam tersi, ...). Açıkça görüldüğü gibi, arayüz (giriş / çıkış parametreleri) değişirse, müşteri tam yükseltme yapmak zorunda kalır.

Bununla birlikte, belirtilen StackOverflow URL'sinde belirtildiği gibi, App_Inst1 veya DLL'lerden birinin basit bir yeniden derlemesi tüm sistemin parçalanmasına neden olabilir, bu nedenle STL (C ++ Standart Şablonu) kullanılmasını önererek bu yazının orjinal başlığım Kütüphane) büyük uygulamalar için.

Umarım bazı soruları / şüpheleri gidermişimdir.


44
Çalıştırılabilir boyutunuz nedeniyle performans sorunlarınız olduğundan emin misiniz ? Tüm yazılımınızın aynı derleyici ile derlendiğini varsaymanın gerçekçi olup olmadığı (örneğin, derleme sunucusunda bir seferde) ya da gerçekten bağımsız ekiplere ayrılmak isteyip istemediğiniz hakkında bazı detaylar ekleyebilir misiniz?
nvoigt

5
Temel olarak, tüm C ++ projelerinin aynı derleyici sürümünde ve aynı C + derleyici ayarlarıyla, kaynağın tutarlı bir anlık görüntüsünden (sürüm) derlendiğinden emin olmak için özel işi "yapı yöneticisi" ve "sürüm yöneticisi" olan bir kişiye ihtiyacınız var. kod, vb. Tipik olarak bu "sürekli entegrasyon" başlığı altında halledilir. Çevrimiçi arama yaparsanız pek çok makale ve araç bulacaksınız. Eski uygulamalar kendi kendini güçlendirebilir - bir eski uygulama, tüm uygulamaların eski olmasına neden olabilir.
rwong

8
Bağlantılı soruda kabul edilen cevap, sorunun genel olarak C ++ çağrılarıyla olduğunu belirtir. Bu yüzden "C ++ değil STL" yardımcı olmaz, güvenli tarafta olmak için çıplak C ile gitmeniz gerekir (ama aynı zamanda cevapları görün, seri hale getirme daha iyi bir çözümdür).
Frax

52
Gerektiğinde dinamik olarak yükleme ve daha sonra boşaltma, performans sorunlarını çözebilmek için Hangi "performans sorunları"? DLL gibi şeyleri bellekten boşaltarak düzeltilebilecek çok fazla bellek kullanmak dışında herhangi bir sorun bilmiyorum - ve eğer sorun buysa en kolay çözüm sadece daha fazla RAM satın almaktır. Uygulamanızı, gerçek performans darboğazlarını tanımlayacak şekilde mi profillediniz ? Çünkü bu bir XY problemi gibi gözüküyor - belirsiz bir "performans sorunu" var ve birileri çoktan çözüme karar verdi.
Andrew Henle

4
@MaxBarraclough "STL", C ++ Standart Kütüphanesine yerleştirilen şablonlanmış kaplar ve fonksiyonlar için alternatif bir isim olarak mükemmel bir şekilde kabul edilir. Aslında, Bjarne Stroustrup ve Herb Sutter tarafından yazılan C ++ Çekirdek Kuralları, bunlar hakkında konuşurken “STL” ye tekrar tekrar atıfta bulunuyor. Bundan daha yetkili bir kaynak alamazsınız.
Sean Burton

Yanıtlar:


110

Bu çok soğuk bir klasik XY problemi.

Sizin asıl sorun performans sorunları olduğunu. Ancak sorunuz, performans sorunlarının gerçekte nereden geldiğine dair hiçbir profil veya başka değerlendirme yapmadığınızı açıkça ortaya koyuyor. Bunun yerine, kodunuzu DLL'lere bölmenin sorunu sihirli bir şekilde çözeceğini umuyorsunuz (ki bu kayıt için olmayacak) ve şimdi bu çözüm dışı durumun bir yönü hakkında endişelisiniz.

Bunun yerine, asıl sorunu çözmeniz gerekir. Birden fazla çalıştırıcınız varsa, hangisinin yavaşlamaya neden olduğunu kontrol edin. Bu işteyken, kötü bir şekilde yapılandırılmış bir Ethernet sürücüsü ya da onun gibi bir şey değil, tüm işlem sürelerini gerçekten de uyguladığınızdan emin olun. Bundan sonra, kodunuzdaki çeşitli görevleri profillemeye başlayın. Yüksek hassasiyetli zamanlayıcı burada senin arkadaşın. Klasik çözüm, bir kod parçası için ortalama ve en kötü durum işleme sürelerini izlemektir.

Verileriniz olduğunda, sorunla nasıl başa çıkabileceğinizi öğrenebilir ve ardından nerede optimize edeceğinizi hesaplayabilirsiniz.


54
"Bunun yerine, kodunuzu DLL'lere bölmenin sorunu sihirli bir şekilde çözeceğini umuyorsunuz (kayıt için olmayacak)" - +1 bunun için. Kişisel işletim sistemi neredeyse kesinlikle uygular elde sayfalama talep tam olarak yükleme ve boşaltma dll işlevselliği, yalnızca otomatik ziyade manuel müdahale gerektiren aynı sonucu. Bir kod parçasının ne kadar uzun süre kullanılmayacağını tahmin etmekte daha iyi olsanız bile, işletim sistemi sanal bellek sisteminden (ki bu pek mümkün değildir) kullanıldığında, işletim sistemi DLL dosyasını önbelleğe alır ve çabalarınızı yine de olumsuzlar .
Jules

@Jules Güncellemeye bakın - DLL'lerin yalnızca ayrı makinelerde bulunduğunu açıkladılar, bu yüzden belki de bu çözümü çalıştırabilirim. Artık iletişim yükü var ama emin olmak çok zor.
Izkata

2
@Izkata - hala tamamen açık değil, ama bence tarif edilen, dinamik ya da yerel (uzak) her bir fonksiyonun bir sürümünü (çalışma zamanı konfigürasyonuna göre) seçmek istedikleri. Ancak, belirli bir makinede hiç kullanılmamış olan EXE dosyasının herhangi bir kısmı yalnızca belleğe yüklenmez, bu nedenle DLL'lerin bu amaçla kullanılması gerekmez. Sadece standart yapıdaki tüm fonksiyonların her iki versiyonunu da ekleyin ve her bir fonksiyonun uygun sürümünü çağırmak için bir fonksiyon işaretçisi (veya C ++ çağrılabilir nesneler veya hangi yöntemi tercih ederseniz) bir tablo oluşturun.
Jules

38

Bir yazılımı birden fazla fiziksel makine arasında bölmek zorunda kalırsanız, veriler arasında makineler arasında veri aktarırken bazı serileştirme formlarına sahip olmanız gerekir, çünkü bazı durumlarda gerçekte makineler arasında aynı ikiliyi gönderebilirsiniz. Serileştirme yöntemlerinin çoğunda STL tipleri ile ilgili herhangi bir problem yoktur, bu yüzden bu durum beni endişelendirecek bir şey değildir.

Bir uygulamayı Paylaşılan Kitaplıklara (DLL) bölmek zorundaysanız (bunu performans nedenleriyle yapmadan önce, gerçekten performans sorunlarını çözeceğinden emin olmalısınız), STL nesnelerini iletmek bir sorun olabilir ama olmak zorunda değildir. Sağladığınız bağlantıda açıklandığı gibi, aynı derleyiciyi ve aynı derleyici ayarlarını kullanıyorsanız STL nesnelerini iletmek işe yarar. Kullanıcılar DLL dosyaları sağlarsa, bu hesaba kolayca güvenemeyebilirsiniz. Ancak tüm DLL'leri sağlar ve her şeyi birlikte derlerseniz, buna güvenebilir ve DLL sınırlarında STL nesnelerini kullanmak çok mümkün olabilir. STL'ye özgü bir sorun olmasa da, derleyici ayarlarına dikkat etmeniz gerekiyor, böylece nesne sahipliğini iletirseniz birden fazla farklı yığın elde edemezsiniz.


1
Evet, ve özellikle tahsis edilen nesneleri DLL / sınırlar arasında geçirme bölümü. Genel olarak, çoklu tahsisatçı sorunundan kesinlikle kaçınmanın tek yolu, yapıyı tahsis eden DLL'nin (ya da kütüphanenin!) Aynı zamanda onu serbest bırakmasını sağlamaktır. Bu nedenle, bu şekilde yazılmış çok sayıda C tarzı API görüyorsunuz: tahsis edilen bir dizi / yapıyı geri alan her API için açık ve ücretsiz bir API. STL ile ilgili ek sorun, arayan kişinin, geçen karmaşık veri yapısını değiştirebilmeyi (eleman eklemesi / kaldırması) bekleyebileceği ve buna da izin verilemeyeceğidir. Ama zorlaması zor.
davidbak

1
Böyle bir uygulamayı bölmek zorunda kalsaydım, muhtemelen COM kullanırdım, ancak bu genellikle her bir C ve C ++ kütüphanesini getirdiğinden kod boyutunu arttırır (aynı olduğunda paylaşılabilir ancak gerektiğinde birbirinden farklılaşabilirlerdi. örneğin, geçişler sırasında, bunun OP'nin sorunu için uygun bir hareket
Simon Richter

2
Özel bir örnek olarak, programın başka bir makineye metin göndermek istediği bir yerde olması muhtemeldir . Bir noktada, o metni temsil eden bazı karakterlerin bir göstergesi olacak. Kesinlikle olamaz sadece bu işaretçiler bit iletmek ve alıcı tarafında tanımlı davranışları bekliyoruz
Caleth

20

Burada, daha büyük ve daha da büyüyen bir sunucu uygulaması üzerinde çalışıyoruz, hatta farklı parçalara bölmeyi düşündüğümüz noktada (DLL), gerektiğinde dinamik olarak yükleme ve daha sonra boşaltma işlemi yapabilmek için performans sorunları

RAM ucuzdur ve bu nedenle aktif olmayan kod ucuzdur. Kod yükleme ve boşaltma (özellikle boşaltma) kırılgan bir işlemdir ve programlarınızın modern masaüstü / sunucu donanımı üzerindeki performansını önemli ölçüde etkilemesi muhtemel değildir.

Önbellek daha pahalıdır, ancak bu kullanılmayan bellekte oturan kodu değil, yalnızca son zamanlarda etkin olan kodu etkiler.

Genel olarak programlar, bilgisayarlarını kod boyutu yerine veri boyutu veya CPU zamanı nedeniyle büyütür. Kod büyüklüğünüz çok büyük sorunlara neden olacak kadar büyüyorsa, muhtemelen ilk başta bunun neden olduğunu görmek istersiniz.

Ancak, kullandığımız fonksiyonlar, giriş ve çıkış parametresini STL nesneleri olarak iletiyor ve bu StackOverflow URL'sinde belirtildiği gibi, bu çok kötü bir fikir.

Görevler ve çalıştırılabilir dosyalar aynı derleyici ile oluşturulduktan ve aynı C ++ çalışma zamanı kitaplığına dinamik olarak bağlandığı sürece sorun çözülmemelidir. Uygulama ve bununla ilişkili görevlerin tek bir ünite olarak oluşturulması ve konuşlandırılması durumunda sorun yaşanmaması gerektiği izlenir.

Problem olabileceği yer, kütüphanelerin farklı kişiler tarafından inşa edildiği veya ayrı olarak güncellenebildiği zamandır.

Tek bir bilgisayarla artık başa çıkamayacak kadar büyük olabilecek bir uygulama oluşturmayı düşünüyorsanız, STL'yi hiç bir teknoloji olarak kullanmamanız gerektiği sonucuna varılabilir mi?

Pek sayılmaz.

Bir uygulamayı birden fazla makineye yaymaya başladığınızda, verileri bu makineler arasında nasıl aktardığınızla ilgili tüm düşünceleriniz vardır. STL tipleri veya daha fazla temel tiplerin kullanılıp kullanılmadığına ilişkin detayların gürültüde kaybolması muhtemeldir.


2
Aktif olmayan kod muhtemelen ilk etapta RAM'e asla yüklenmez. Çoğu işletim sistemi, gerçekten gerekli olması durumunda yalnızca çalıştırılabilir sayfalardan sayfa yükler.
Jules

1
@Jules: Ölü kod canlı kodla karıştırılmışsa (sayfa boyutu = 4k boyutunda), o zaman eşleştirilir + yüklenir. Önbellek daha ince (64B) ayrıntı derecesinde çalışır, bu nedenle kullanılmayan işlevlerin çok fazla acı vermediği hala doğrudur. Her sayfa, ancak bir çalışma zamanı kaynağı olan ve (RAM'in aksine) bir TLB girişine ihtiyaç duyar. (Dosya destekli eşlemeler tipik olarak en azından Linux'ta hugepage kullanmaz; Bir hugepage x86-64'te 2MiB'dir, böylece hugepage'lerde TLB özlemleri olmadan çok daha fazla kod veya veri kapsayabilir.)
Peter Cordes

1
Ne @PeterCordes notları: Öyleyse, “PGO” yu sürüm oluşturma işleminizin bir parçası olarak kullandığınızdan emin olun!
JDługosz

13

Hayır, sonucun geldiğini sanmıyorum. Programınız birden fazla makineye dağıtılmış olsa bile, STL'yi dahili olarak kullanmanın onu modüller arası / proses iletişiminde kullanmaya zorlamasının bir nedeni yoktur.

Aslında, harici arayüzlerin tasarımını baştan beri iç uygulamalardan ayırmanız gerektiğini iddia ediyorum, zira eski içinde dahili olarak kullanılanlarla karşılaştırıldığında değişmesi daha sağlam / zor olacak


7

Bu sorunun noktasını kaçırıyorsunuz.

Temelde iki tür DLL vardır. Kendiniz ve bir başkasının. "STL problemi", sizin ve onların aynı derleyiciyi kullanmıyor olabileceğidir. Açıkçası, bu kendi DLL dosyanız için bir sorun değil.


5

DLL'leri aynı kaynak ağacından aynı derleyici ve derleme seçenekleriyle aynı anda oluşturuyorsanız, tamam işe yarayacaktır.

Ancak, bir uygulamayı yeniden kullanılabilen birden fazla parçaya bölmenin "Windows aromalı" yolu COM bileşenleridir . Bunlar küçük (tek tek kontroller veya codec bileşenleri) veya büyük olabilir (IE, mshtml.dll dosyasında COM kontrolü olarak kullanılabilir).

gerektiğinde dinamik olarak yükleme ve daha sonra boşaltma

Bir sunucu uygulaması için bu muhtemelen korkunç bir etkinliğe sahip olacak ; bu yalnızca gerçekten uzun bir zaman diliminde birden fazla aşamadan geçen bir uygulamaya sahip olduğunuzda uygulanabilir, böylece bir daha ne zaman ihtiyaç duyulmayacağını anlayabilirsiniz. Bana bindirme mekanizmasını kullanan DOS oyunlarını hatırlatıyor.

Ayrıca, sanal bellek sisteminiz düzgün çalışıyorsa, kullanılmayan kod sayfalarını arayarak bu işlemi sizin için halledecektir.

bir bilgisayarın artık kaldıramayacağı kadar büyük olabilir

Daha büyük bir PC satın alın.

Unutma , doğru optimizasyon ile bir dizüstü bilgisayar bir hadoop kümesinden daha iyi performans gösterebilir.

Gerçekten birden fazla sisteme ihtiyacınız varsa , seri hale getirme maliyetinin olduğu için aralarındaki sınır hakkında çok dikkatli düşünmeniz gerekir. MPI gibi çerçevelere bakmaya başlamanız gereken yer burasıdır.


1
"yalnızca çok uzun bir süre boyunca birden fazla aşamadan geçen bir uygulamanız olduğunda, bir şey bir daha gerekmeyeceklerini bilmeniz için gerçekten uygulanabilir" - o zaman bile çok yardımcı olması mümkün değildir, çünkü OS DLL dosyalarını önbellekleyin; bu, büyük olasılıkla doğrudan çalıştırılabilir dosyalara doğrudan dahil olan işlevlerden daha fazla bellek harcar. Bindirmeler yalnızca sanal belleği olmayan sistemlerde veya sanal adres alanı sınırlayıcı faktör olduğunda yararlıdır (Bu uygulamanın 64 bit olduğunu sanıyorum, 32 değil ...).
Jules

3
"Daha büyük bir PC al" +1. Artık birden çok terabayt RAM'a sahip sistemler satın alabilirsiniz . Tek bir geliştiricinin saatlik ücretinden daha az bir ücret karşılığında Amazon'dan bir tane kiralayabilirsiniz. Bellek kullanımını azaltmak için kodunuzu optimize etmek için ne kadar geliştirici zaman harcıyorsunuz?
Jules

2
Karşılaştığım en büyük sorun, "daha büyük bir bilgisayar satın almak" sorusuyla "uygulamanız ne kadar ölçeklenecek?" Sorusuyla ilgiliydi. Cevabım şuydu: “Test yapmak için ne kadar para harcamak istiyorsunuz?” tek CPU'lu bir bilgisayarın yapabildiğini. " Birçok eski programcının PC'lerin ne kadar büyüdüğü konusunda gerçekçi bir fikri yoktur; Modern PC'lerde tek başına video kartı 20. yüzyıl standartlarına göre bir süper bilgisayar.
MSalters

COM bileşenleri? 1990'larda belki, ama şimdi?
Peter Mortensen

@ MSalters - right ... Bir uygulamanın tek bir PC'de ne kadar ölçeklenebileceği ile ilgili herhangi bir sorusu olan herkes Amazon EC2 x1e.32xlarge örnek tipinin özelliklerine bakmalıdır - makinede toplam 128 sanal çekirdek sağlayan 72 fiziksel işlemci çekirdeği 2.3GHz (3.1GHz'e kadar burstable), potansiyel olarak 340GB / s bellek bant genişliği (ne tür belleğin takılı olduğuna, özelliklerinde açıklanmayan türüne bağlı olarak) ve 3.9 TB RAM'e bağlı olarak. Ana RAM'e dokunmadan çoğu uygulamayı çalıştırmak için yeterli önbelleğe sahiptir. Hatta bir GPU olmadan 2000 den 500 düğüm süper küme kadar güçlü olduğunu
Jules

0

Burada, daha büyük ve daha da büyüyen bir sunucu uygulaması üzerinde çalışıyoruz, hatta farklı parçalara bölmeyi düşündüğümüz noktada (DLL dosyaları), gerektiğinde dinamik olarak yükleme ve daha sonra boşaltma işlemleri yapabilmek için performans sorunları.

İlk bölüm mantıklı (performans nedenleriyle farklı makinelere bölme uygulaması).

İkinci bölüm (kütüphanelerin yüklenmesi ve boşaltılması) anlamsızdır, çünkü bu fazladan çaba sarf eder ve (gerçekten) bir şeyleri iyileştirmez.

Tarif ettiğiniz sorun, özel hesaplama makineleriyle daha iyi çözülür, ancak bunlar aynı (ana) uygulama ile çalışmamalıdır.

Klasik çözüm şuna benzer:

[user] [front-end] [machine1] [common resources]
                   [machine2]
                   [machine3]

Ön uç ve hesaplama makineleri arasında, yük dengeleyici ve performans izleme gibi ekstra şeylere sahip olabilirsiniz ve özel makinelerde özel işlem yapmak önbellekleme ve verim optimizasyonları için iyidir.

Bu, hiçbir şekilde DLL'lerin yüklenmesini / boşaltılmasını veya STL ile ilgisi olmadığını gösterir.

Yani, gerektiğinde dahili olarak STL'yi kullanın ve verilerinizi öğeler arasında seri hale getirin (bkz. Grpc ve protokol arabellekleri ve çözdükleri türler).

Bu, sağladığınız sınırlı bilgi ile, bunun klasik xy problemi gibi göründüğünü söyledi (@Graham'ın dediği gibi).

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.