Modern OO dilleri C ++ 'ın dizi mağaza performansı ile rekabet edebilir mi?


40

En azından biraz aşina olduğum her modern OO programlama dilinin (temelde sadece Java, C # ve D) kovaryant dizilere izin verdiğini fark ettim. Yani, bir dize dizisi bir nesne dizisidir:

Object[] arr = new String[2];   // Java, C# and D allow this

Kovariant diziler statik tip sistemde bir deliktir. Derleme zamanında algılanamayan yazım hatalarını mümkün kılarlar, bu nedenle bir diziye her yazım çalışma zamanında kontrol edilmelidir:

arr[0] = "hello";        // ok
arr[1] = new Object();   // ArrayStoreException

Çok sayıda dizi mağaza yaparsam bu korkunç bir performans hit gibi görünüyor.

C ++ 'in kovaryant dizileri yoktur, bu nedenle böyle bir çalışma zamanı kontrolü yapmaya gerek yoktur, yani performans cezası yoktur.

Gerekli çalışma zamanı kontrollerinin sayısını azaltmak için herhangi bir analiz yapıldı mı? Örneğin, şunu söylersem:

arr[1] = arr[0];

biri, mağazanın başarısız olamayacağını iddia edebilir. Eminim düşünemediğim birçok başka optimizasyon vardır.

Modern derleyiciler aslında bu tür optimizasyonlar yapıyor mu, yoksa örneğin bir Quicksort'un her zaman O (n log n) gereksiz çalışma zamanı kontrolleri yapması gerçeğiyle yaşamak zorunda mıyım?

Modern OO dilleri, eş değişkenli dizileri destekleyerek oluşturulan ek yükleri önleyebilir mi?


7
Neden C ++ 'ın, C ++' ın desteklemediği bir özellikte diğer dillerden daha hızlı olduğunu önerme konusunda kafanız karışık.

17
@eco: C ++ hızlı dizi erişimi olan çünkü o eş varyant diziler desteklemez. Fred, "modern OO dillerinin" eş değişken dizilerinin ek yükünü C ++ hızlarına yaklaştırmasının mümkün olup olmadığını bilmek istiyor.
Mooing Duck

13
"Çalışma zamanında derleme yapan ancak istisnalar atan kod kötü haber"

4
@Jesse Milyonlarca insan, dinamik dillerde güvenilir, yüksek oranda ölçeklenebilir kodlar yazmaktadır. İmo: Kodunuz için test senaryoları yazmazsanız, derleyici hataları olup olmadığını umursamıyorum, hiçbir şekilde ona güvenmeyeceğim.
Voo

7
@Jesse Başka ne zaman istisnalar beklersin ama çalışma zamanında? Sorun, çalışma zamanında istisnalar ortaya koyan bir kod değildir - bunun çok mantıklı olduğu durumlar vardır - sorun, derleyici tarafından statik olarak yakalanmayan ancak bunun yerine bir istisna ile sonuçlanan yanlış olduğu garantilen koddur . Çalışma süresi.
Jonathan M Davis,

Yanıtlar:


33

D gelmez eşdeğişkin diziler var. En son sürümden önce izin verdiler ( dmd 2.057 ), ancak bu hata giderildi.

D'deki bir dizi etkili bir şekilde sadece bir işaretçi ve uzunluğa sahip bir yapıdır:

struct A(T)
{
    T* ptr;
    size_t length;
}

Sınır dizileri normalde bir dizini indekslerken yapılır, ancak derleme yaptığınızda kaldırılır -release. Bu nedenle, serbest bırakma modunda, C / C ++ dizileriyle D dizileri arasında gerçek bir performans farkı yoktur.


- değilse görünür d-programming-language.org/arrays.html Statik dizisi" diyor T[dim]...: örtük aşağıdakilerden birine dönüştürülebilir U[]... Dinamik bir dizi T[]dolaylı aşağıdakilerden birine dönüştürülebilir: U[]. .. Utemel sınıf nerede T. "
Ben Voigt

2
@BenVoigt O zaman çevrimiçi dokümanlar güncellenmelidir. Maalesef her zaman% 100 güncel değiller. Dönüşüm ile çalışacak const U[]o zaman dizinin elemanları türü yanlış atanamıyor çünkü, fakat T[]yok kesinlikle değil dönüştürmek U[]uzun olduğu kadar U[]değişkendir. Daha önce olduğu gibi kovaryant dizilere izin vermek, şimdi düzeltilmiş olan ciddi bir tasarım hatasıydı.
Jonathan M Davis,

@JonathanMDavis: Kovariant dizileri çok serttir, ancak sadece aynı diziden okunan bir dizi elemanına yazacak olan kod senaryosunda iyi çalışırlar . D, rastgele türden dizileri sıralayabilecek bir yöntem yazmasına nasıl izin verir?
Süpermen

@supercat İsteğe bağlı türler sıralayan bir işlev yazmak istiyorsanız, onu şablon haline getirin. örn. dlang.org/phobos/std_algorithm.html#sort
Jonathan M Davis

21

Evet, önemli bir optimizasyon şudur:

sealed class Foo
{
}

C # 'da, bu sınıf herhangi bir tip için bir üst tip olamaz, bu nedenle bir tür dizinin denetlenmesini önleyebilirsiniz Foo.

Ve ikinci soruya göre, F # eş değişkenli dizilere izin verilmez (ancak çalışma zamanında optimizasyonda gereksiz olmadığı sürece kontrolün CLR'de kalacağını tahmin ediyorum)

let a = [| "st" |]
let b : System.Object[] = a // Fails

https://stackoverflow.com/questions/7339013/array-covariance-in-f

Biraz ilgili bir problem dizi sınırlaması kontrolüdür. Bu, CLR'de yapılan optimizasyonlarla ilgili ilginç (ancak eski) bir okuma olabilir (kovaryans da ayrıca 1 yerden bahsedilir): http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds -kontrol-eliminasyon-in-the-clr.aspx


2
Scala ayrıca bu yapıyı önler: val a = Array("st"); val b: Array[Any] = ayasa dışı. (Ancak, Scala'daki diziler, kullanılan temel JVM nedeniyle ... özel bir sihirdir.)
pst

13

Java cevabı:

Anladığım kadarıyla kodun kıyaslamasını yapmadın, değil mi? Genel olarak, Java'daki tüm dinamik yayınların% 90'ı ücretsizdir, çünkü JIT bunları seçebilir (quicksort bunun için iyi bir örnek olmalıdır) ve gerisi ld/cmp/brkesinlikle tahmin edilebilir olan bir sekanstır (değilse neden cehennem senin kodunu atıyor? tüm bu dinamik döküm istisnaları?).

Yükü fiili kıyaslamadan çok daha erken yapıyoruz, şube tüm durumlarda% 99,9999 (istatistiksel olarak oluşturulmuştur) olarak doğru bir şekilde tahmin edilir, bu nedenle boru hattını (yük ile hafızaya çarpmadığımızı varsayarak) durdurmazız farkedilmeyecek kadar iyi değil, ancak yine de yük gerekli. Bu nedenle, maliyet 1 saat çevrimidir, eğer JIT hiçbir şekilde kontrolden kaçınmazsa.

Bazı ek yükü? Tabii, ama hiç farketmeyeceğinden şüpheliyim ..


Cevabımı desteklemek için lütfen şu Dr. Cliff'e bakın . Java ile C performansını tartışan blog yazısını tıklayın .


10

D, kovaryant dizilere izin vermez.

void main()
{
    class Foo {}
    Object[] a = new Foo[10];
}  

/* Error: cannot implicitly convert expression (new Foo[](10LU)) of type Foo[]
to Object[] */

Söylediğiniz gibi, bu izin vermek için tip sistemde bir delik olacaktır.

Bu hata yalnızca 13 Aralık'ta yayınlanan en son DMD'de düzeltildiği için, affedilmiş olabilirsiniz .

D'deki dizi erişimi, C veya C ++ kadar hızlıdır.


D-programming-language.org/arrays.html "e göre statik bir dizi T [dim], aşağıdakilerden birine dolaylı olarak dönüştürülebilir: ... U [] ... Dinamik bir dizi T [] örtük olarak dönüştürülebilir şunlardan birine: U [] ... ki burada U, T'nin temel sınıfıdır. "
Ben Voigt

1
@BenVoigt: Tarihi geçmiş belgeler.
BCS

1
@BenVoigt: Belgeleri güncellemek için bir çekme isteği oluşturdum. Umarım bu yakında çözülecek. Gösterdiğin için teşekkürler.
Peter Alexander

5

Test itibaren, ucuz bir dizüstü bilgisayarda kullanmak arasındaki fark yapmış int[]ve Integer[]1.0 ns hakkındadır. Aradaki fark, türün ekstra kontrolünden kaynaklanıyor olabilir.

Genellikle, nesneler, her ns sayılmadığında yalnızca üst seviye mantık için kullanılır. Her ns'yi kaydetmeniz gerekirse, Objects gibi daha üst düzey yapıları kullanmaktan kaçınırdım. Tek başına atamalar herhangi bir gerçek programda çok küçük bir faktör olması muhtemeldir. örneğin, aynı makinede yeni bir Nesne oluşturma 5 ns'dir.

Karşılaştırma yapmak için yapılan çağrılar çok daha pahalı olabilir, özellikle de String gibi karmaşık bir nesne kullanıyorsanız.


2

Diğer modern OO dillerini sordunuz mu? Şey, Delphi bu sorunu tamamen stringilkel, bir nesne değil olarak önleyerek önler . Bu nedenle, bir dizi dizisi tam olarak bir dizi dizisidir ve başka bir şey değildir ve bunlarla ilgili herhangi bir işlem, genel gider türünü kontrol etmeden, yerel kodun yapabileceği kadar hızlıdır.

Ancak, dizi dizileri çok sık kullanılmaz; Delphi programcıları TStringListsınıfı tercih eder . Asgari miktarda ek yük için, sınıfın İsviçre Çakısıyla karşılaştırıldığı birçok durumda yararlı olan bir dizi string grubu yöntemi sağlar. Bu yüzden bir dize dizisi yerine bir dize listesi nesnesi kullanmak deyimseldir.

Genel olarak diğer nesnelere gelince, sorun mevcut değil çünkü Delphi'de, C ++ 'da olduğu gibi, burada açıklanan tipte sistem deliklerinin önlenmesi için diziler kovaryant değil.


1

veya bir Quicksort'un her zaman O (n log n) gereksiz çalışma zamanı kontrolleri yapması gerçeğiyle mi yaşamak zorundayım?

CPU performansı monotonik değildir; bu, daha uzun programların daha kısa programlardan daha hızlı olabileceği anlamına gelir (bu CPU'ya bağımlıdır ve ortak x86 ve amd64 mimarileri için geçerlidir). Bu nedenle, dizilerde sınır kontrolü yapan bir programın, bu sınır denetimi kaldırılarak önceki programdan düşülen programdan daha hızlı olması mümkündür.

Bu davranışın nedeni sınır kontrolünün bellekteki kod hizalamasını değiştirmesi, önbellek isabet sıklığı vb.

Bu yüzden evet, Quicksort'un her zaman O (n log n) sahte kontrolleri yapması ve profil oluşturduktan sonra optimize etmesi gerçeğiyle yaşayın.


1

Scala, kovaryanttan ziyade değişmez dizilere sahip bir OO dilidir. JVM'yi hedeflemektedir, bu nedenle orada performans kazanımı yoktur, ancak Java ve C # için ortak bir yanlışlıktan kaçınılması, türlerin güvenliğini geriye dönük uyumluluk nedenleriyle tehlikeye sokar.

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.