Bu "özelliği" başka hiçbir yerde görmedim. 32. bitin çöp toplama için kullanıldığını biliyorum. Ama neden diğer temel türler için değil de yalnızca ints için bu şekilde?
Bu "özelliği" başka hiçbir yerde görmedim. 32. bitin çöp toplama için kullanıldığını biliyorum. Ama neden diğer temel türler için değil de yalnızca ints için bu şekilde?
Yanıtlar:
Buna etiketli işaretçi gösterimi denir ve onlarca yıldır birçok farklı yorumlayıcıda, sanal makinede ve çalışma zamanı sisteminde kullanılan oldukça yaygın bir optimizasyon hilesidir. Hemen hemen her Lisp uygulaması bunları, birçok Smalltalk sanal makinesini, birçok Ruby yorumlayıcısını vb. Kullanır.
Genellikle, bu dillerde, her zaman işaretçilerin etrafından nesnelere geçersiniz. Bir nesnenin kendisi, nesne meta verilerini (bir nesnenin türü, sınıf (lar) ı, belki erişim kontrol kısıtlamaları veya güvenlik ek açıklamaları vb.) Ve ardından gerçek nesne verilerinin kendisini içeren bir nesne başlığından oluşur. Dolayısıyla, basit bir tamsayı, bir işaretçi artı meta verilerden ve gerçek tam sayıdan oluşan bir nesne olarak temsil edilecektir. Çok kompakt bir gösterimle bile, bu basit bir tamsayı için 6 Bayt gibi bir şeydir.
Ayrıca, hızlı tamsayı aritmetiği gerçekleştirmek için böyle bir tamsayı nesnesini CPU'ya iletemezsiniz. İki tam sayı eklemek istiyorsanız, gerçekten sadece iki işaretçiniz vardır, bunlar eklemek istediğiniz iki tamsayı nesnesinin nesne başlıklarının başlangıcına işaret eder. Bu nedenle, ofseti tamsayı verilerinin depolandığı nesneye eklemek için ilk işaretçide tamsayı aritmetiği gerçekleştirmeniz gerekir. O zaman bu adrese başvurmak zorundasın. Aynısını ikinci tamsayı için tekrar yapın. Artık CPU'dan eklemesini isteyebileceğiniz iki tamsayınız var. Elbette, şimdi sonucu tutmak için yeni bir tamsayı nesnesi oluşturmalısınız.
Yani, bir tamsayı toplamayı gerçekleştirmek için, aslında üç tamsayı toplama artı iki işaretçi dererefence artı bir nesne oluşturma yapmanız gerekir . Ve neredeyse 20 Byte alırsınız.
Bununla birlikte, işin püf noktası, tamsayılar gibi değişmez değer türlerinde , genellikle nesne başlığındaki tüm meta verilere ihtiyacınız olmamasıdır : tüm bunları dışarıda bırakabilir ve basitçe sentezleyebilirsiniz (bu, VM nerd- "Sahte" için konuşun), birisi bakmayı umursadığında. Bir tamsayı her zaman sınıfa sahip olacaktır Integer
, bu bilgiyi ayrı ayrı saklamaya gerek yoktur. Birisi bir tamsayı sınıfına dışarı şekle yansıma kullanıyorsa, sadece cevap Integer
ve kimse aslında nesne başlığında bu bilgiyi depolamak olmadığını bilecek ve bu aslında, orada değil hatta bir nesne başlığı (ya da nesne).
Böylece, trick değeri saklamak için bir işaretçi içindeki nesnenin için etkili bir şekilde tek, iki çöken, nesne.
Bir işaretçi (sözde etiket bitleri ) içinde, işaretçi hakkında ekstra bilgi depolamanıza izin veren gerçekte ek alana sahip CPU'lar vardır . "Bu aslında bir işaretçi değil, bu bir tam sayıdır" gibi ekstra bilgiler. Örnekler arasında Burroughs B5000, çeşitli Lisp Makineleri veya AS / 400 bulunur. Ne yazık ki, mevcut ana CPU'ların çoğu bu özelliğe sahip değil.
Bununla birlikte, bir çıkış yolu var: mevcut ana CPU'ların çoğu, adresler kelime sınırları üzerinde hizalanmadığında önemli ölçüde daha yavaş çalışıyor. Hatta bazıları hizalanmamış erişimi desteklemiyor.
Bunun anlamı, pratikte tüm işaretçilerin 4'e bölünebileceği, yani her zaman iki 0
bitle biteceği anlamına gelir . Bu bize gerçek işaretçiler (biten 00
) ile kılık değiştirmiş tamsayılar (ile bitenler) arasında ayrım yapmamızı sağlar 1
. Ve yine de bizi 10
başka şeyler yapmak için ücretsiz olarak biten tüm işaretçilerle bırakıyor . Ayrıca, çoğu modern işletim sistemi çok düşük adresleri kendilerine ayırır, bu da bize uğraşmamız gereken başka bir alan sağlar (örneğin, 24 0
s ile başlayan ve biten işaretçiler 00
).
Dolayısıyla, 31 bitlik bir tamsayıyı basitçe 1 bit sola kaydırıp ekleyerek bir işaretçi olarak kodlayabilirsiniz 1
. Ve bunlarla çok hızlı tamsayı aritmetiği gerçekleştirebilirsiniz , onları uygun şekilde kaydırarak (bazen bu bile gerekli değildir).
Diğer adres alanlarıyla ne yapıyoruz? Tipik örnekler arasında kodlama bulunurfloat
diğer büyük adres alanı ve benzeri özel nesnelerin bir dizi s true
, false
, nil
, yakın 127 ASCII karakterleri, yaygın olarak kullanılan bazı kısa dizeleri, boş liste boş nesne, boş dizi ve benzeri 0
adres.
Örneğin, MRI, YARV ve Rubinius Ruby yorumlayıcılarında, tamsayılar yukarıda tanımladığım şekilde false
kodlanır, adres olarak kodlanır 0
(ki bu aynı zamandafalse
C'deki temsilidir ), true
adres olarak 2
(ki bu da öyle olur true
bir bit kaydırılmış C gösterimi ) ve nil
as 4
.
int
.
İyi bir açıklama için https://ocaml.org/learn/tutorials/performance_and_profiling.html'nin "tam sayıların, etiket bitlerinin, yığın ayrılan değerlerin gösterimi" bölümüne bakın .
Kısa cevap, performans için olmasıdır. Bir işleve bir bağımsız değişken iletilirken, ya bir tamsayı ya da bir işaretçi olarak iletilir. Makine seviyesinde bir dil seviyesinde, bir kaydın tamsayı veya işaretçi içerip içermediğini anlamanın bir yolu yoktur, sadece 32 veya 64 bitlik bir değerdir. Dolayısıyla, OCaml çalışma zamanı, aldığı şeyin tam sayı mı yoksa işaretçi mi olduğunu belirlemek için etiket bitini kontrol eder. Etiket biti ayarlanmışsa, değer bir tamsayıdır ve doğru aşırı yüklemeye aktarılır. Aksi takdirde bir göstericidir ve yazı yukarı bakılır.
Neden bu etikete sadece tamsayılar sahip? Çünkü diğer her şey bir işaretçi olarak aktarılır. İletilen şey ya bir tamsayı ya da başka bir veri türüne göstericidir. Yalnızca bir etiket biti ile yalnızca iki durum olabilir.
Tam olarak "çöp toplama için kullanılmaz". Bir işaretçi ile kutusuz bir tamsayıyı dahili olarak ayırt etmek için kullanılır.
OP'nin 64-bit OCaml için 63-bit kayan nokta tipini daha fazla anlamasına yardımcı olmak için bu bağlantıyı eklemeliyim
Makalenin başlığı hakkında görünse float
de aslındaextra 1 bit
OCaml çalışma zamanı, türlerin tek tip gösterimi yoluyla polimorfizme izin verir. Her OCaml değeri tek bir kelime olarak temsil edilir, böylece bu listelere erişilecek (örn. List.length) ve derlenecek (örn. List.map) işlevlerle "şeylerin listesi" için tek bir uygulamaya sahip olmak mümkündür. bunlar ister tam sayıların, ister kayan sayıların listeleri, ister tam sayı kümeleri listeleri olsun, aynı şekilde çalışır.
Bir kelimeye sığmayan her şey, öbek içindeki bir blokta tahsis edilir. Bu veriyi temsil eden kelime daha sonra bloğa bir göstericidir. Yığın yalnızca kelime blokları içerdiğinden, tüm bu işaretçiler hizalanır: en önemsiz birkaç biti her zaman ayarlanmamıştır.
Argümansız kurucular (şunun gibi: meyve türü = Elma | Portakal | Muz) ve tamsayılar, yığın içinde ayrılması gereken çok fazla bilgiyi temsil etmez. Temsilleri kutudan çıkarılmıştır. Veriler, aksi takdirde bir işaretçi olacak olan kelimenin doğrudan içindedir. Bu nedenle, bir liste listesi aslında işaretçilerin bir listesi iken, bir dizi ints, bir eksi yönlendirme ile tam metinleri içerir. Girişler ve işaretçiler aynı boyutta olduğundan, listelere erişen ve oluşturan işlevler fark edilmez.
Yine de, Çöp Toplayıcının işaretçileri tam sayılardan tanıyabilmesi gerekir. Bir işaretçi, öbek içindeki iyi biçimlendirilmiş bir bloğa işaret eder ve bu bloğun tanımı gereği canlıdır (GC tarafından ziyaret edildiğinden) ve bu şekilde işaretlenmelidir. Bir tamsayı herhangi bir değere sahip olabilir ve önlem alınmazsa yanlışlıkla bir işaretçi gibi görünebilir. Bu, ölü blokların canlı görünmesine neden olabilir, ancak daha da kötüsü, aslında bir işaretçi gibi görünen ve kullanıcıyı karıştıran bir tamsayıyı takip ederken, GC'nin canlı bir bloğun başlığı olduğunu düşündüğü şeydeki bitleri değiştirmesine neden olur. veri.
Kutusuz tam sayıların OCaml programlayıcıya 31 bit (32 bit OCaml için) veya 63 bit (64 bit OCaml için) sağlamasının nedeni budur. Temsilde, perde arkasında, bir tamsayı içeren bir kelimenin en önemsiz biti, onu bir işaretçiden ayırmak için her zaman ayarlanır. 31 veya 63 bitlik tam sayılar oldukça sıra dışıdır, bu nedenle OCaml kullanan herkes bunu bilir. OCaml kullanıcılarının genellikle bilmediği şey, 64-bit OCaml için 63-bit kutusuz kayan tipin neden olmadığıdır.
OCaml'de bir int neden yalnızca 31 bittir?
Temel olarak, baskın işlemin desen eşleştirme olduğu ve baskın veri türlerinin varyant türleri olduğu Coq teorem kanıtlayıcı üzerinde mümkün olan en iyi performansı elde etmek için. En iyi veri temsilinin, işaretçileri kutulu olmayan verilerden ayırmak için etiketleri kullanan tek tip bir temsil olduğu bulunmuştur.
Ama neden diğer temel türler için değil de yalnızca ints için bu şekilde?
Sadece değil int
. char
Ve numaralandırmalar gibi diğer türler aynı etiketli gösterimi kullanır.