Dizeler neden bu kadar yavaş?


23

Lisedeki ilk programlama dersimden beri, sicim operasyonlarının efsanevi "ortalama operasyon" dan daha yavaş - yani daha maliyetli - olduğunu duydum. Neden onları bu kadar yavaş yapıyor? (Bu soru bilerek geniş bırakılmıştır.)


11
Bu "ortalama operasyonların" efsanevi olduğunu biliyorsanız, en azından bazılarının ne olduğunu bize söyleyebilir misiniz? Böyle belirsiz bir soru sorduğunuza göre, belirtilmemiş bu işlemlerin gerçekten efsanevi olduğu iddiasına güvenmek zor.
seh

1
@seh, ne yazık ki, aslında buna cevap veremiyorum. İnsanlara gerçekte hangi dizgelerin daha yavaş olduğunu sorduğum birkaç kez, sadece biraz omuz silkti ve "sadece yavaşlar" dedi. Ayrıca, daha spesifik bilgiye sahip olsaydım, bu SO için bir soru olurdu, Programcılar için değil; çoktan sınırda zaten.
Pops

Amaç ne ? Söylenen dizeler gerçekte yavaşsa, onları kullanmayı keser misiniz?
Tulains Córdova

Unut gitsin. Birisi size saçma sapan söylerse, karşı soru şöyledir: "Gerçekten mi? Öyle mi? O zaman bir int dizi mi kullanmalıyız?"
Ingo

Yanıtlar:


47

"Ortalama operasyon" ilkellerde gerçekleşir. Ancak, dizgelerin ilkel olarak kabul edildiği dillerde bile, yine de kaputun altındaki dizilerdir ve tüm dizeyi içeren bir şey yapmak, N'nin dizenin uzunluğu olduğu O (N) zaman alır.

Örneğin, iki sayı eklemek genellikle 2-4 ASM komutunu alır. İki dizeyi birleştirmek ("ekleme"), yeni bir bellek ayırma ve dizenin tamamını içeren bir veya iki dizi kopya gerektirir.

Bazı dil faktörleri daha da kötüleştirebilir. Örneğin, C'de bir dize yalnızca boş olarak sonlandırılmış bir karakter dizisine bir göstericidir. Bu, ne kadar süreceğini bilmediğiniz anlamına gelir, bu nedenle hızlı kopyalama işlemleriyle dizge kopyalama döngüsünü optimize etmenin bir yolu yoktur; bir seferde bir karakter kopyalamanız gerekir, böylece boş sonlandırıcı için her baytı test edebilirsiniz.


4
Bazı diller bunu daha iyi hale getirir: Delphi'nin dizinin başında dize uzunluğunu kodlaması dize birleştirmeyi çok hızlı yapar.
Frank Shearar

4
@gablin: Aynı zamanda dizginin kendisini daha hızlı kopyalamasını da sağlar. Öndeki büyüklüğü bildiğinizde, bir seferde bir bayt kopyalamanız ve her baytı boş bir sonlandırıcı için kontrol etmeniz gerekmez, böylece veri hareketi için SIMD olanlar da dahil olmak üzere herhangi bir yazıcının tam boyutunu kullanabilirsiniz. 16 kata kadar daha hızlı.
Mason Wheeler

4
@mathepic: Evet ve sen libc'nizdeki veya diğer harici kod ile etkileşim başladığınızda kadarıyla o götürür, ancak olduğunca iyi, bu bir beklediğini char*değil, bir strbufve kare 1'e ediyoruz için bu kadar sadece var sen dilde kötü bir tasarım yapıldığında yapabilir.
Mason Wheeler

6
@ mathepic: Tabii ki bufişaretçi orada. Asla uygun olmadığını ima etmek istemedim; bunun yerine, gerekli. Standart kütüphane kadar temel olan şeyler de dahil olmak üzere, optimize edilmiş ancak standart dışı dize türünüz hakkında hiçbir şey bilmeyen kodların yavaş ve güvensiz olarak geri çekilmesi gerekir char*. İsterseniz bu FUD'yi arayabilirsiniz, ancak bu doğru değildir.
Mason Wheeler

7
Millet, Frank Shearer'in konusu hakkında bir Joel Spolsky sütunu var: Back to Basics
user16764

14

Bu eski bir iş parçacığı ve diğer cevapların harika olduğunu düşünüyorum ama bir şeyi görmezden gelin, işte benim (geç) 2 sentim.

Sözdizimsel Şeker Kaplama Karmaşıklığı Gizler

İplerle ilgili sorun, çoğu dilde ikinci sınıf vatandaş olmalarıdır ve aslında çoğu zaman dil spesifikasyonunun bir parçası değildir: en üstte zaman zaman bazı sözdizimsel şeker kaplamalı bir kütüphane uygulamasıdır. Onları kullanmak için daha az acı çekmek.

Bunun doğrudan sonucu dilin karmaşıklığının çok büyük bir bölümünü görüşünüzden uzak tutması ve sinsi yan etkilerin karşılığını ödemektir, çünkü onları tıpkı tıpkı bir gibi düşük seviyeli atomik bir varlık gibi düşünme alışkanlığı haline gelirsiniz diğer ilkel türler (en çok oy alan cevap ve diğerleri tarafından açıklandığı gibi).

Uygulama ayrıntıları

İyi Ol 'Dizi

Bu altta yatan "karmaşıklık" öğelerinin bir tanesi, dize uygulamalarının çoğunun, dizeyi temsil etmek için bitişik bir bellek alanına sahip basit bir veri yapısı kullanmaya başvurmasıdır: dizininiz.

Bu iyi bir anlam ifade ediyor, dizeye bir bütün olarak hızlı erişimin olmasını istediğiniz gibi, dikkat edin. Ancak bu dizgiyi değiştirmek istediğinizde potansiyel olarak korkunç maliyetler anlamına gelir. Ortadan bir elemana erişmek, hangi indeksin peşinde olduğunuzu biliyorsanız, ancak bir koşulu temel alan bir öğe aramak hızlı olmayabilir.

Diliniz dizenin uzunluğunu önbelleğe almıyorsa ve karakterleri saymak için içinden geçmesi gerekiyorsa, dizenin boyutunu döndürmek bile maliyetli olabilir.

Benzer nedenlerden dolayı, dizginize eleman eklemek , büyük olasılıkla bu işlemin gerçekleşmesi için bir miktar bellek ayırmanız gerekeceğinden masraflı olacaktır.

Dolayısıyla farklı diller bu konulara farklı yaklaşımlar getiriyor. Örneğin Java, bazı geçerli nedenlerle (önbellek uzunluğu, iş güvenliği) ve değişken meslektaşları için (StringBuffer ve StringBuilder) dizelerini sabit hale getirme özgürlüğüne kavuştu; Her zaman, ancak en iyi durum senaryoları için umut. Genel olarak iyi çalışır, ancak aşağı tarafı bazen bellek etkileri için ödeme yapmaktır.

Unicode Desteği

Ayrıca, ve bu, yine de, dilinizin sözdizimsel şeker kaplamasının bunu sizden hoş görünmesini engellemesinden kaynaklanmaktadır, genellikle unicode destek terimleriyle düşünmüyorsunuz (özellikle gerçekten ihtiyaç duymadığınız sürece) ve o duvara çarptı). Bazı diller, ileri görüşlü olarak, 8-bitlik basit ilkel karakterlerin altında yatan dizileri olan dizeleri uygulamıyor. UTF-8 veya UTF-16'da pişirilmişler ya da sizin için neyin size destek veriyorlar ve bunun sonucu, çoğu zaman gerekmeyen muazzam derecede daha büyük bir bellek tüketimi ve bellek ayırmak, dizeleri işlemek için daha büyük bir işlem süresidir. ve kod noktalarını değiştirmekle el ele giden bütün mantığı uygulayın.


Tüm bunların sonuçları, sözde kodda eşdeğer bir şey yaptığınızda:

hello = "hello,"
world = " world!"
str = hello + world

- dil geliştiricilerin sizin dışında davranmalarını sağlamak için harcadıkları tüm çabalara rağmen - basit bir şekilde:

a = 1;
b = 2;
shouldBeThree = a + b

Bir takip olarak, okumak isteyebilirsiniz:


Mevcut tartışmaya iyi bir ek.
Abel

Bunun en iyi cevap olduğunu henüz fark ettim çünkü efsanevi ifade RSA şifrelemesi gibi bir şeye uygulanabiliyorsa yavaş. İpin bu utanç verici noktaya yerleştirilmesinin tek nedeni, çoğu dilde dizgiler için sağlanan artı operatörün, yeni başlayanları işlemin arkasındaki maliyetten habersiz yapmasıdır.
Codism

@Abel: teşekkürler, bana daha genel detaylar için oda gibiydi.
haylem

@Codism: teşekkürler, sevdiğine sevindim. Bunun, sadece bir karmaşıklık meselesinin gizlendiği bir çok olaya uygulanabileceğini düşünüyorum (ve sonunda bir tür darboğaz veya tuğla duvara çarptığımızdan, artık ihtiyacımız olana kadar daha düşük seviye detaylara fazla dikkat etmiyoruz). ).
haylem

1

Teorik bir Random-Access Stored-Program makinesinin tek bir işlemi için "ortalama işlem" tabiri kısaca açıktır . Bu, çeşitli algoritmaların çalışma zamanını analiz etmek için geleneksel olarak kullanılan teorik makinedir.

Genel işlemler normal olarak yükleme, toplama, çıkarma, saklama, dallanma olarak alınır. Belki de okuyun, yazdırın ve durdurun.

Ancak çoğu string işlemi bu temel işlemlerin birkaçını gerektirir. Örneğin, bir dizgenin çoğaltılması normal olarak bir kopyalama işlemi gerektirir ve bu nedenle bir dizgenin uzunluğuyla orantılı olan bir dizi işlem (yani, "doğrusal" dır). Başka bir dizgide bir alt dizgenin bulunması da doğrusal karmaşıklığa sahiptir.


1

Tamamen işleme, dizelerin nasıl temsil edildiğine ve hangi optimizasyonların olduğuna bağlıdır. Dizeler 4 veya 8 bayt uzunluğundaysa (ve hizalıysa), mutlaka daha yavaş olmaz - çoğu işlem ilkel kadar hızlı olur. Veya, tüm dizelerin bir 32-bit veya 64-bit karma değeri varsa, birçok işlem de aynı kadar hızlı olur (karma maliyetini siz öderseniz de).

Aynı zamanda "yavaş" ile ne demek istediğine de bağlı. Çoğu program, gerek duyulan şey için dizeleri çok hızlı bir şekilde işler. Dize karşılaştırmaları iki girişi karşılaştırmak kadar hızlı olmayabilir, ancak yalnızca profil oluşturma, programınız için "yavaş" ın ne anlama geldiğini ortaya çıkarır.


0

Sorunuza bir soru ile cevap vereyim. Neden bir kelime dizisi söylemek, tek bir kelime söylemekten daha uzun sürüyor?


2
Bu mutlaka gerekmez.
user16764

3
Supercalifragilisticexpialidocious
Spoike

s / word / hece / g
Caleb

Soru-cevabınızı bir soru ile cevaplayayım: neden cevabınızın ne anlama geldiğini söylemiyorsunuz? Sonuçta, bazı çalışma zamanı sistemlerine nasıl uygulandığı şeklinde yorumlanabileceğinden çok uzak.
PJTraill
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.