V8 geliştiricisi burada. Bu soruya olan ilgi miktarı ve diğer cevapların eksikliği göz önüne alındığında, bunu denebilirim; Korkarım ki umduğun cevap bu olmayacak.
Paketlenmiş SMI dizileri dünyasında kalırken nasıl programlanacağına dair bir dizi yönerge var mı (örneğin)?
Kısa cevap: işte burada: const guidelines = ["keep your integers small enough"]
.
Daha uzun cevap: çeşitli nedenlerle kapsamlı bir kılavuz seti vermek zordur. Genel olarak, JavaScript geliştiricilerinin kendileri ve kullanım durumları için anlamlı bir kod yazmaları ve JavaScript motoru geliştiricilerinin bu kodun motorlarında nasıl hızlı çalıştırılacağını anlamaları gerektiğidir. Kapak tarafında, motor uygulama tercihleri ve optimizasyon çabalarına bakılmaksızın, bazı kodlama modellerinin her zaman diğerlerinden daha yüksek performans maliyetlerine sahip olacağı anlamında, bu ideale ilişkin bazı sınırlamalar vardır.
Performans tavsiyesi hakkında konuştuğumuzda, bunu aklımızda tutmaya çalışıyoruz ve hangi önerilerin birçok motorda ve uzun yıllar boyunca geçerli kalma olasılığının yüksek olduğunu ve makul olarak deyimsel / müdahaleci olmadığını dikkatli bir şekilde tahmin ediyoruz.
Eldeki örneğe dönersek: Smis'i dahili olarak kullanmanın, kullanıcı kodunun bilmesi gerekmeyen bir uygulama detayı olduğu varsayılır. Bazı vakaları daha verimli hale getirecek ve diğer durumlarda zarar vermemelidir. Tüm motorlar Smis kullanmaz (örneğin, AFAIK Firefox / Spidermonkey tarihsel olarak kullanmadı; Bazı durumlarda bu günlerde Smis kullandıklarını duydum; ancak hiçbir ayrıntı bilmiyorum ve herhangi bir otorite ile konuşamıyorum madde). V8'de Smis'in boyutu dahili bir ayrıntı ve aslında zamanla ve üzeri versiyonlarda değişiyor. Eskiden çoğunluk kullanım durumu olan 32 bit platformlarda, Smis her zaman 31 bit işaretli tam sayı olmuştur; 64 bit platformlarda eskiden en yaygın durum gibi görünen 32 bit işaretli tam sayılardı, Chrome 80'de "işaretçi sıkıştırması" gönderene kadar Smi boyutunu 32 bit platformlardan bilinen 31 bite indirmeyi gerektiren 64 bit mimariler için. Smis'in tipik olarak 32 bit olduğu varsayımına dayalı bir uygulama yapmış olsaydınız,bu .
Neyse ki, belirttiğiniz gibi, çift diziler hala çok hızlı. Sayısal-ağır kod için, çift dizileri varsaymak / hedeflemek muhtemelen mantıklıdır. JavaScript'te çiftlerin yaygınlığı göz önüne alındığında, tüm motorların çiftler ve çift diziler için iyi bir desteğe sahip olduğunu varsaymak mantıklıdır.
Vec.add () gibi şeyleri çağrı sitelerine satır içine almak için makro sistem gibi bir şey kullanmadan Javascript'te yüksek performanslı genel programlama yapmak mümkün müdür?
"jenerik" genellikle "yüksek performans" ile çelişmektedir. Bu JavaScript veya belirli motor uygulamaları ile ilgisi yoktur.
"Genel" kod, kararların çalışma zamanında alınması gerektiği anlamına gelir. Bir işlevi her yürüttüğünüzde, " x
tamsayı mıdır? " Sorusunu belirlemek için kodun çalıştırılması gerekir. Öyleyse, bu kod yolunu alın. x
Bir dize mi? Sonra buraya atlayın. Bu bir nesne mi .valueOf
? belki .toString()
? Belki prototip zincirinde mi? Bunu çağırın ve sonucu ile baştan başlayın ". "Yüksek performanslı" optimize edilmiş kod esasen tüm bu dinamik kontrolleri düşürme fikri üzerine inşa edilmiştir; bu sadece motorun / derleyicinin tiplerini önceden çıkarmanın bir yolu olduğunda mümkündür: x
her zaman bir tamsayı olacağını kanıtlayabilirse (veya yeterince yüksek olasılıkla varsayalım), bu durumda sadece kod üretmesi gerekir ( kanıtlanmamış varsayımların söz konusu olup olmadığına dair bir tür kontrol tarafından korunmaktadır).
Inlining tüm bunlara diktir. "Genel" bir işlev hala satır içine alınabilir. Bazı durumlarda, derleyici, burada polimorfizmi azaltmak için tip bilgisini satır içi fonksiyona yayabilir.
(Karşılaştırma için: C ++, statik olarak derlenmiş bir dil olarak, ilgili bir sorunu çözmek için şablonlara sahiptir. Kısacası, programcının derleyiciye belirli türlerde parametreleştirilmiş işlevlerin (veya tüm sınıfların) özel kopyalarını oluşturmasını açıkça bildirmesine izin verir. Örneğin bazı durumlarda için değil, sakıncaları kendi set vermeden güzel bir çözüm, uzun derleme kez ve büyük ikili. JavaScript, tabii ki, şablonlar diye bir şey vardır. Şunları kullanabilirsiniz eval
size daha sonra biraz benzer bir sistem inşa etmek, ancak benzer dezavantajlarla karşılaşırsanız: çalışma zamanında C ++ derleyicisinin çalışmasının eşdeğerini yapmanız ve oluşturduğunuz kod miktarı konusunda endişelenmeniz gerekir.)
Megamorfik çağrı siteleri ve deoptimizasyonlar gibi şeyler ışığında yüksek performanslı kodu nasıl libarilere dönüştürür? Örneğin, yüksek hızda mutlu bir şekilde Doğrusal Cebir A paketini kullanıyorsam ve sonra A'ya bağlı bir B paketini içe aktarırsam, ancak B diğer türlerle çağırır ve deoptimize eder, aniden (kodum değişmeden) kodum daha yavaş çalışır .
Evet, bu JavaScript ile ilgili genel bir sorundur. V8 Array.sort
, JavaScript'te belirli yerleşikleri (şeyler gibi ) dahili olarak uygulardı ve bu sorun ("geri bildirim kirliliği" olarak adlandırdığımız), bu teknikten tamamen uzaklaşmamamızın temel nedenlerinden biriydi.
Bununla birlikte, sayısal kod için, o kadar çok tür (sadece Smis ve çiftler) yoktur ve belirttiğiniz gibi, pratikte benzer performansa sahip olmaları gerekir, bu nedenle tip geri besleme kirliliği gerçekten teorik bir endişe olur ve bazı durumlarda önemli bir etkiye sahipse, lineer cebir senaryolarında ölçülebilir bir fark görmemeniz de muhtemeldir.
Ayrıca, motorun içinde "bir tip == hızlı" ve "birden fazla tip == yavaş" durumundan çok daha fazla durum vardır. Belirli bir operasyon hem Smis'i hem de iki katını gördüyse, bu tamamen iyi. İki tür diziden eleman yüklemek de iyidir. Bir yükün, onları tek tek izlemekten vazgeçtiği kadar çok farklı tür gördüğü ve bunun yerine çok sayıda türe daha iyi ölçeklenen daha genel bir mekanizma kullandığı durum için "megamorfik" terimini kullanıyoruz - bu tür yükleri içeren bir işlev hala optimize edilebilir. Bir "deoptimizasyon", bir işlev için optimize edilmiş kodu atmak zorunda kalmanın çok özel bir eylemidir, çünkü daha önce görülmemiş ve optimize edilmiş kodun işlemek için donanımlı olmadığı yeni bir tür görülür. Ama bu bile iyi: daha fazla tür geribildirimi toplamak için optimize edilmemiş koda geri dönün ve daha sonra tekrar optimize edin. Bu birkaç kez olursa, endişelenecek bir şey yoktur; sadece patolojik olarak kötü durumlarda bir sorun haline gelir.
Yani tüm bunların özeti: endişelenmeyin . Sadece makul bir kod yazın, motorun bununla ilgilenmesine izin verin. Ve "makul" ile kastediyorum: kullanım durumunuz için anlamlı olan, okunabilir, bakımı kolay, verimli algoritmalar kullanır, dizilerin uzunluğunun ötesinde okuma gibi hatalar içermez. İdeal olarak, hepsi bu kadar, ve başka bir şey yapmanıza gerek yok. Eğer yapacak daha iyi hissetmeni sağlayacaksa şey , ve / veya aslında performans sorunları gözlemleyerek eğer, ben iki fikri sunabilir:
TypeScript kullanmak yardımcı olabilir . Büyük yağ uyarısı: TypeScript türleri, yürütme performansını değil geliştirici verimliliğini hedefler (ve ortaya çıktıkça, bu iki perspektifin bir tür sisteminden çok farklı gereksinimleri vardır). Bununla birlikte, bazı çakışmalar vardır: örneğin, sürekli olarak açıklama eklerseniz number
, derleyici, null
yalnızca sayıları içermesi / çalıştırması gereken bir dizi veya işleve yanlışlıkla girerseniz sizi uyarır . Tabii ki, disiplin hala gereklidir: tek bir number_func(random_object as number)
kaçış kapağı sessizce her şeyi zayıflatabilir, çünkü tip ek açıklamalarının doğruluğu hiçbir yerde uygulanmaz.
TypedArrays kullanmak da yardımcı olabilir. Normal JavaScript dizilerine kıyasla dizi başına biraz daha fazla ek yüke (bellek tüketimi ve ayırma hızı) sahiptir (bu nedenle, çok sayıda küçük diziye ihtiyacınız varsa, normal diziler muhtemelen daha verimlidir) ve daha az esnektirler çünkü büyümezler veya ayırmadan sonra küçülürler, ancak tüm elemanların tam olarak bir tipte olduğunu garanti ederler.
Javascript motorunun dahili olarak türlerle ne yaptığını kontrol etmek için kullanımı kolay ölçüm araçları var mı?
Hayır, bu kasıtlı. Yukarıda açıklandığı gibi, kodunuzu V8'in bugün özellikle iyi optimize edebileceği her şekle göre uyarlamanızı istemiyoruz ve bunu da gerçekten yapmak istediğinize inanmıyoruz. Bu şeyler her iki yönde de değişebilir: Kullanmak istediğiniz bir desen varsa, bunu gelecekteki bir sürümde optimize edebiliriz (daha önce kutulanmamış 32 bit tam sayıları dizi öğeleri olarak saklama fikriyle oynamıştık.) ama bununla ilgili çalışmalar henüz başlamadı, bu yüzden vaat yok); ve bazen geçmişte optimize etmek için kullandığımız bir model varsa, diğer daha önemli / etkili optimizasyonların önüne geçerse bunu bırakmaya karar verebiliriz. Ayrıca, satır içi sezgisel tarama gibi şeyleri doğru yapmak herkesin bildiği gibi zor, bu yüzden doğru satır içi kararı doğru zamanda almak devam eden bir araştırma alanı ve motor / derleyici davranışında bunlara karşılık gelen değişiklikler; bu da bunu herkes için talihsiz olacağı başka bir durum haline getiriyor (sizve biz) bazı mevcut tarayıcı sürümleri setini yaklaşık olarak düşündüğünüz (veya bildiğiniz?) en iyi karar verene kadar kodunuzu değiştirmek için çok fazla zaman harcadıysanız, yalnızca o zamanki mevcut tarayıcıları gerçekleştirmek için yarım yıl sonra geri gelmek buluşsal yöntemlerini değiştirdi.
Elbette, uygulamanızın performansını her zaman bir bütün olarak ölçebilirsiniz - sonuçta önemli olan budur, özellikle motorun dahili olarak yaptığı seçimler değil. Mikrobenchmarklara dikkat edin, çünkü yanıltıcıdırlar: sadece iki satır kod çıkarırsanız ve bunları karşılaştırırsanız, senaryonun motorun çok farklı kararlar vereceğinden yeterince farklı olması (örneğin, farklı tür geri bildirimleri).