Python'ın dize interning mekanizması, bellek içinde her farklı dize değeri için yalnızca bir kopya saklar, böylece sözlük anahtar karşılaştırmaları, karakter-karakter karşılaştırmalar yerine işaretçi eşitliği kontrollerine kısa devre yapabilir. CPython derleyicisi yalnızca harfler, rakamlar ve alt çizgiler içeren kimlik benzeri dize literalleriyle karşılaştığında, bunları derleme zamanında otomatik olarak intern eder ve bunları küresel bir interned sözlüğünde saklar. Bu optimizasyon, sözlük arama algoritmasının önce nesne kimliğini is operatörünü kullanarak test etmesine, daha sonra daha maliyetli == karşılaştırmasına dönmesine olanak tanır; bu da anahtar eşleşmelerinin zaman karmaşıklığını O(n) 'den O(1)'e önemli ölçüde azaltır. Ancak, kullanıcı girişi veya birleştirme gibi çalıştırma zamanında oluşturulan rastgele dizeler otomatik olarak intern edilmez, ancak eğer zaten mevcut değilse sys.intern() ile açıkça geçirilirse intern tablosuna eklenir. Bu mekanizma, intern edilmiş dizelerin kimlik tabanlı karşılaştırmalar için güvenli kalmasını sağlamak üzere Python'ın dize nesnelerinin değişmezliğine dayanır.
Bir geliştirme ekibi, her saat milyarlarca JSON yükünü işleyen yüksek verimli bir telemetri hizmeti inşa ediyordu; bunlar her biri "timestamp", "event_type", ve "user_id" gibi tekrar eden dize anahtarlarını içeriyordu. Yük testleri sırasında, bellek profillemesi, yığın belleğin %35'inin bu aynı anahtarlar için tekrar eden dize nesneleri tarafından işgal edildiğini ortaya koydu, CPU profillemesi ise sözlük ekleme ve arama işlemlerinde PyUnicode_RichCompare'de önemli zaman harcandığını gösterdi. Dar boğaz, standart sözlük algoritmasının bu sık tekrar eden anahtarlar için dize içeriklerini karşılaştırmasından kaynaklanıyordu, bellek adreslerini değil.
Düşünülen bir çözüm, her anahtar üzerinde JSON ayrıştırma aşamasında manuel olarak sys.intern() çağrısı yapmaktı. Bu yaklaşım, tüm aynı anahtarların aynı bellek adresini paylaşmasını garanti ederdi, bu da kimlik karşılaştırmaları yoluyla mümkün olan en hızlı sözlük işlemlerini sağlardı. Ancak, ekip bunun Python 3.6'da küresel intern tablosunda önemli kilit çatışması yarattığını ve intern edilmiş dizelerin yorumlayıcı kapanana kadar devam ettiğinden bellek büyümesine neden olabileceğini fark etti; bu da sürekli yük altında hizmetin çökmesine neden olabilirdi.
Diğer bir yaklaşım, uygulama katmanında dize örneklerini yeniden kullanmak için özel bir nesne havuzu veya flyweight deseni uygulamaktı; bu şekilde küresel intern tablosuna güvenmek yerine. Bu strateji, havuzlanmış dizelerin yaşam döngüsü üzerinde daha fazla kontrol sundu ve kalıcı bellek tahsisini önledi ancak tüm sözlük erişim desenlerinin sarılması gerekiyordu ve standart Python kütüphaneleriyle uyumluluğu bozuyordu. Ek karmaşıklık ve bakım yükümlülüğü, bu belirli mimari için performans faydalarını aştı.
Ekip, sonunda, sys.intern()'ı yalnızca 50 yüksek frekanslı anahtardan oluşan önceden tanımlı bir küme üzerine uygulayan bir ayrıştırma ara katmanı uygulayarak hibrid beyaz liste yaklaşımını seçti ve kilit çatışmasını azaltmak için Python 3.10'a yükseldi. Bu karar, bellek verimliliği ile güvenlik endişeleri arasında bir denge sağladı ve %40'lık bir yığın kullanımı azalması ve %18'lik bir istek verimliliği artışı ile sonuçlandı. Bu optimizasyon, hizmet düzeyi hedeflerini gerçekleştirmek ve sistemin zirve yük koşulları altında kararlılığını sürdürmek için kritik öneme sahip oldu.
is ile karşılaştırılan iki aynı dize literalinin, otomatik olarak intern edilmiş olmalarına rağmen bazen False döndürmesi neden olur?
Bu durum, CPython'ın derleyicisinin dizeleri yalnızca aynı kod nesneleri içinde sabitler olarak veya modül derleme sırasında kimlik desenlerine uyduğunda intern etmesinden kaynaklanır. Etkileşimli kabinelerde, her satır ayrı bir kod nesnesi olarak derlendiği için, farklı satırlarda yazılan aynı literal'lar farklı bellek adreslerinde yer alabilir. Ayrıca, ancak ASCII karakterleri içermeyen veya rakamla başlamayan dizeler otomatik olarak intern edilmeyebilir, bu da is karşılaştırmalarının başarısız olmasına neden olabilir, oysa == başarılı olabilir.
Güvenilmeyen kullanıcı girişlerinden kaynaklanan dizelerin intern edilmesinin bellek yönetimi üzerindeki etkileri nelerdir ve bu potansiyel bir hizmet engelleme vektörü neden oluşturur?
İntern edilmiş dizeler CPython'da ölümsüzdür, yani hiç çöp toplanmazlar ve yorumlayıcı işlem süresi boyunca devam ederler. Bir uygulama rastgele kullanıcı girişlerini intern ederse —örneğin kullanıcı adları, e-posta adresleri veya arama sorguları— her benzersiz dize kalıcı olarak bellek tüketir ve geri alınamaz. Bir saldırgan, milyonlarca benzersiz dize yükü göndererek bunu istismar edebilir ve sonuç olarak mevcut RAM’i tüketerek işlemi çökertir; bu nedenle, intern etmeden önce girişleri temizlemek veya beyaz listeye almak kritik önem taşır.
hash() fonksiyonu, sözlük ekleme işlemleri sırasında intern edilmiş dizelerle nasıl etkileşir ve intern etmenin hash değeri hesaplamasını etkileyip etkilemediği?
hash() fonksiyonu, dizenin içeriğine dayalı olarak değerini hesaplar, kimliği veya intern edilmiş durumunu göz önünde bulundurmaz, yani intern etme, bir dizenin hash değerini değiştirmez. Ancak, CPython'ın sözlük uygulaması, hash değerlerini karşılaştırdıktan sonra nesne kimliğini (is) kontrol eden bir optimizasyon içerir, daha sonra tam eşitlik karşılaştırmasına (==) geri döner. Kimlik olarak aynı olan intern edilmiş dizeler için, bu kimlik kontrolü hemen True döndürür ve O(n) karakter karşılaştırmasını atlar; ancak adaylar genellikle intern etmenin kendisinin hash algoritmasını değiştirdiğine inanarak bunu karıştırırlar.