Haben Sie jemals versucht, alle Zahlen von 1 bis 2.000.000 in Ihrer bevorzugten Programmiersprache zusammenzufassen? Das Ergebnis kann leicht manuell berechnet werden: 2.000.001.000.000, was etwa 900-mal größer ist als der Maximalwert einer 32-Bit-Ganzzahl ohne Vorzeichen.
C # druckt aus -1453759936
- ein negativer Wert! Und ich denke, Java macht das Gleiche.
Das bedeutet, dass es einige gängige Programmiersprachen gibt, die den arithmetischen Überlauf standardmäßig ignorieren (in C # gibt es versteckte Optionen, um dies zu ändern). Das ist ein Verhalten, das für mich sehr riskant aussieht, und wurde der Absturz von Ariane 5 nicht durch einen solchen Überlauf verursacht?
Also: Was sind die Entwurfsentscheidungen hinter solch einem gefährlichen Verhalten?
Bearbeiten:
Die ersten Antworten auf diese Frage drücken die übermäßigen Überprüfungskosten aus. Führen wir ein kurzes C # -Programm aus, um diese Annahme zu testen:
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);
Auf meinem Computer dauert die aktivierte Version 11015 ms, während die nicht aktivierte Version 4125 ms dauert. Das heißt, die Überprüfungsschritte dauern fast doppelt so lange wie das Hinzufügen der Zahlen (insgesamt das Dreifache der ursprünglichen Zeit). Bei den 10.000.000.000 Wiederholungen beträgt der Zeitaufwand für eine Überprüfung jedoch immer noch weniger als 1 Nanosekunde. Es kann Situationen geben, in denen dies wichtig ist, aber für die meisten Anwendungen spielt dies keine Rolle.
Bearbeiten 2:
Ich habe unsere Serveranwendung (ein Windows-Dienst, der Daten analysiert, die von mehreren Sensoren empfangen wurden, einige davon sind auffällig) mit dem /p:CheckForOverflowUnderflow="false"
Parameter neu kompiliert (normalerweise schalte ich die Überlaufprüfung ein) und auf einem Gerät implementiert. Die Nagios-Überwachung zeigt, dass die durchschnittliche CPU-Auslastung bei 17% blieb.
Dies bedeutet, dass der im obigen Beispiel festgestellte Leistungstreffer für unsere Anwendung völlig irrelevant ist.
(1..2_000_000).sum #=> 2000001000000
. Eine andere meiner Lieblings Sprachen: sum [1 .. 2000000] --=> 2000001000000
. Nicht mein Favorit: Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000
. (Um fair zu sein, der letzte betrügt.)
Integer
in Haskell ist willkürlich genau. Es kann eine beliebige Zahl gespeichert werden, solange Ihnen nicht der zuweisbare Arbeitsspeicher ausgeht.
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.
Das ist ein Hinweis darauf, dass die Schleife optimiert wird. Auch dieser Satz widerspricht früheren Zahlen, die mir sehr zutreffend erscheinen.
checked { }
Abschnitt verwenden, um die Teile des Codes zu markieren, die arithmetische Überlaufprüfungen durchführen sollen. Dies ist auf die Leistung zurückzuführen