Hiç favori programlama dilinizdeki 1 ila 2.000.000 arasındaki tüm sayıları toplamaya çalıştınız mı? Sonuç elle hesaplamak kolaydır: işaretsiz bir 32bit tamsayının maksimum değerinden 900 kat daha büyük olan 2,000,001,000,000.
C # yazdırır -1453759936
- negatif bir değer! Ve sanırım Java da aynı şeyi yapıyor.
Bu, varsayılan olarak Aritmetik Taşma'yı görmezden gelen bazı ortak programlama dilleri olduğu anlamına gelir (C # dilinde, bunu değiştirmek için gizli seçenekler vardır). Bu benim için çok riskli görünen bir davranış ve bu tür bir taşma nedeniyle Ariane 5'in çöküşü değil miydi?
Öyleyse: Böylesine tehlikeli bir davranışın arkasındaki tasarım kararları nelerdir?
Düzenle:
Bu sorunun ilk cevapları, aşırı kontrol maliyetlerini ifade ediyor. Bu varsayımı test etmek için kısa bir C # programı çalıştıralım:
Stopwatch watch = Stopwatch.StartNew();
checked
{
for (int i = 0; i < 200000; i++)
{
int sum = 0;
for (int j = 1; j < 50000; j++)
{
sum += j;
}
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
Makinemde işaretli sürüm 11015ms, denetlenmeyen sürüm 4125ms. Diğer bir deyişle, kontrol adımları numaraların eklenmesiyle neredeyse iki katına çıkar (toplamda orijinal sürenin 3 katı). Ancak 10.000.000.000 tekrarla, çekle geçen zaman hala 1 nanosaniyeden az. Bunun önemli olduğu bir durum olabilir, ancak çoğu uygulama için bunun önemi yoktur.
Düzenleme 2:
Sunucu uygulamamızı (birkaç sensörden gelen verilerin analizini yapan bir Windows servisi, oldukça fazla sayıda kırılmaya neden oldu) /p:CheckForOverflowUnderflow="false"
parametre ile (normalde taşma kontrolünü açtım) yeniden derledim ve bir cihaza yerleştirdim. Nagios izleme, ortalama CPU yükünün% 17'de kaldığını gösteriyor.
Bu, yukarıdaki telafi örneğinde bulunan performans isabetinin uygulamamızla tamamen alakasız olduğu anlamına gelir.
(1..2_000_000).sum #=> 2000001000000
. Benim favori dillerden biri daha: sum [1 .. 2000000] --=> 2000001000000
. Benim favori: Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000
. (Adil olmak gerekirse, sonuncusu hile yapıyor.)
Integer
Haskell'deki @BernhardHiller isteğe bağlı olarak duyarlıdır , ayrılabilir RAM'lerin bitmediği sürece herhangi bir sayı tutacaktır.
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.
Bu, optimize edilen döngünün bir göstergesidir. Ayrıca bu cümle benim için çok geçerli görünen önceki rakamlarla çelişiyor.
checked { }
, kodun Aritmetik Taşma kontrollerini yapması gereken bölümlerini işaretlemek için bölümü kullanabilirsiniz . Bu performans nedeniyle