Hallo,
wie im Titel schon gesagt, würde mich interessieren was schneller ist:
double a;
double b;
...
if(Math.Abs(a)<b)
{
...
}
//oder:
if(a<b && a>-b)
{
...
}
Da ich diese Abfrage sehr sehr oft habe und ich nun daran interessiert bin meinen Code zu optimieren.
Viele Grüße
Quaneu
Hallo,
es hindert dich nichts dies selbst auszuprobieren und die Lösung hier zu posten 😁
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo,
solange es dabei nciht extrem riesige unterschiede in der performance gibt (was mich sehr sehr wundern würde...)
Stimmt. Es gibt auch kaum einen Unterschied. Das sind sehr elementare Operationen und da gibts nicht viel zu holen.
Verwende die Math.Abs
-Variante -> ist m.E. besser lesbar und auf den 1. Blick sofort erkenntlich was gemacht werden soll.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hatte gehofft, dass schon wer Erfahrung gesammelt hat.
Hab das jetzt mal so getestet:
double a;
int i = 0, s = 0;
long abs_Time = 0;
long and_Time = 0;
Random ran;
Stopwatch _watch = new Stopwatch();
for (s = 0; s < 100; s++)
{
_watch.Start();
for (i = 0; i < 50000; i++)
{
ran = new Random(i);
a = ran.NextDouble() - 2;
if (Math.Abs(a) < 0.5)
{
a += ran.NextDouble();
}
}
_watch.Stop();
abs_Time += _watch.ElapsedMilliseconds;
_watch.Start();
for (i = 0; i < 50000; i++)
{
ran = new Random(i);
a = ran.NextDouble() - 2;
if (a < 0.5 && a > -0.5)
{
a += ran.NextDouble();
}
}
_watch.Stop();
and_Time += _watch.ElapsedMilliseconds;
}
MessageBox.Show((abs_Time/100).ToString() + "\n" + (and_Time/100).ToString());
Das Ergebnis war :
47929
48408
Wobei ich gesagt hätte, dass die and-Methode schneller wäre... oder teste ich falsch?
UPDATE:
Sowohl im Debug als auch im Release sind die Ergebnisse die gleichen.
Daher werde ich euren Ratschlag gerne annehmen und weiter mit Math.Abs() arbeiten.
Vielen Dank
oder teste ich falsch?
Erstens erzeugst du die Zufallszahlen während der Zeitmessung (verfälscht das Ergebnis), zweitens sind die Eingabemengen für beide Funktionen nicht gleich (verfälscht das Ergebnis) und drittens - am gravierendsten - du resettest die StopWatch nicht.
Hallo,
oder teste ich falsch?
Ja. Du erzeugts immer ein neues Random-Objekt innerhalb der Messung (das wäre auch sonst nicht notwendig). Somit kann eine eventuelle GC die Messung verfälschen.
Siehe auch dN!3Ls-Antwort.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
@Quaneu: Solche Unterschiede herauszufinden, ist immer interessant, vor allem natürlich wenn es um die Performance geht. Oft hilft dabei auch ein Blick in den IL-Code (Byte-Code).
s. dazu auch:
Disassemblieren von Projekte, ganz einfach mit ILDASM [VS2005 Deutsch]
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Hallo dr4g0n76,
Oft hilft dabei auch ein Blick in den IL-Code (Byte-Code).
Und in den Maschinencode (CLR-Debugger - Release-Modus) zum Abschätzen der Operationen die für eine Aufgabe notwendig sind. Der IL-Code ist dabei oft noch auf eine zu hohen Level.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
@Gfoidl: Das wäre dann meiner Meinung nach der nächste Schritt für ihn gewesen.
😉
für z.B.
public void Test1()
{
int a = 7;
int b = 2904;
bool bTest1 = Math.Abs(a) < b;
}
kommt heraus:
.method public hidebysig instance void Test1() cil managed
{
// Code size 20 (0x14)
.maxstack 2
.locals init ([0] int32 a,
[1] int32 b,
[2] bool bTest1)
IL_0000: nop
IL_0001: ldc.i4.7
IL_0002: stloc.0
IL_0003: ldc.i4 0xb58
IL_0008: stloc.1
IL_0009: ldloc.0
IL_000a: call int32 [mscorlib]System.Math::Abs(int32)
IL_000f: ldloc.1
IL_0010: clt
IL_0012: stloc.2
IL_0013: ret
} // end of method Form1::Test1
und für test2:
public void Test2()
{
int a = 7;
int b = 2904;
bool bTest2 = a < b && a > -b;
}
.method public hidebysig instance void Test2() cil managed
{
// Code size 23 (0x17)
.maxstack 2
.locals init ([0] int32 a,
[1] int32 b,
[2] bool bTest2)
IL_0000: nop
IL_0001: ldc.i4.7
IL_0002: stloc.0
IL_0003: ldc.i4 0xb58
IL_0008: stloc.1
IL_0009: ldloc.0
IL_000a: ldloc.1
IL_000b: bge.s IL_0014
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: neg
IL_0010: cgt
IL_0012: br.s IL_0015
IL_0014: ldc.i4.0
IL_0015: stloc.2
IL_0016: ret
} // end of method Form1::Test2
EDIT: muss noch mal den richtigen Code nachliefern statement ist falsch.
EDIT: korrigiert.
Das wird aus Test 1 (Release):
public void Test1()
{
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,40h
00000009 mov esi,ecx
0000000b lea edi,[ebp-38h]
0000000e mov ecx,0Bh
00000013 xor eax,eax
00000015 rep stos dword ptr es:[edi]
00000017 mov ecx,esi
00000019 xor eax,eax
0000001b mov dword ptr [ebp-1Ch],eax
0000001e mov dword ptr [ebp-3Ch],ecx
00000021 cmp dword ptr ds:[04AC3D60h],0
00000028 je 0000002F
0000002a call 75F68F91
0000002f xor edx,edx
00000031 mov dword ptr [ebp-44h],edx
00000034 xor edx,edx
00000036 mov dword ptr [ebp-40h],edx
00000039 mov dword ptr [ebp-48h],0
00000040 nop
int a = 7;
00000041 mov dword ptr [ebp-40h],7
int b = 2904;
00000048 mov dword ptr [ebp-44h],0B58h
bool bTest1 = Math.Abs(a) < b;
0000004f mov ecx,dword ptr [ebp-40h]
00000052 call FF87F03C
00000057 mov dword ptr [ebp-4Ch],eax
0000005a mov eax,dword ptr [ebp-4Ch]
0000005d cmp eax,dword ptr [ebp-44h]
00000060 setl al
00000063 movzx eax,al
00000066 mov dword ptr [ebp-48h],eax
}
00000069 nop
0000006a lea esp,[ebp-0Ch]
0000006d pop ebx
0000006e pop esi
0000006f pop edi
00000070 pop ebp
00000071 ret
und das aus Test 2 (Release):
public void Test2()
{
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,40h
00000009 mov esi,ecx
0000000b lea edi,[ebp-38h]
0000000e mov ecx,0Bh
00000013 xor eax,eax
00000015 rep stos dword ptr es:[edi]
00000017 mov ecx,esi
00000019 xor eax,eax
0000001b mov dword ptr [ebp-1Ch],eax
0000001e mov dword ptr [ebp-3Ch],ecx
00000021 cmp dword ptr ds:[04AC3D60h],0
00000028 je 0000002F
0000002a call 75F68F09
0000002f xor edx,edx
00000031 mov dword ptr [ebp-40h],edx
00000034 xor edx,edx
00000036 mov dword ptr [ebp-44h],edx
00000039 mov dword ptr [ebp-48h],0
00000040 nop
int a = 7;
00000041 mov dword ptr [ebp-40h],7
int b = 2904;
00000048 mov dword ptr [ebp-44h],0B58h
bool bTest2 = a < b && a > -b;
0000004f mov eax,dword ptr [ebp-40h]
00000052 cmp eax,dword ptr [ebp-44h]
00000055 jge 0000006B
00000057 nop
00000058 mov eax,dword ptr [ebp-44h]
0000005b neg eax
0000005d cmp eax,dword ptr [ebp-40h]
00000060 setl al
00000063 movzx eax,al
00000066 mov dword ptr [ebp-4Ch],eax
00000069 jmp 00000070
0000006b xor edx,edx
0000006d mov dword ptr [ebp-4Ch],edx
00000070 movzx eax,byte ptr [ebp-4Ch]
00000074 mov dword ptr [ebp-48h],eax
}
00000077 nop
00000078 lea esp,[ebp-0Ch]
0000007b pop ebx
0000007c pop esi
0000007d pop edi
0000007e pop ebp
0000007f ret
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
für z.B. [Math.Abs] kommt heraus
Wobei da ja viel interessanter ist, was Math.Abs
macht. Im Double-Fall wird nämlich gar nicht "in IL weitergemacht", sondern es wird eine externe/native Funktion gerufen.
Hab dies extra so gemacht, das er mir nix wegoptimiert. Denn anders hat er die Schleifen anscheinend optimiert.
Und was würde mir dies jetzt sagen? Kenne mich da leider noch zu wenig aus.
Hallo,
Und was würde mir dies jetzt sagen?
Verwende die Math.Abs-Variante -> ist m.E. besser lesbar und auf den 1. Blick sofort erkenntlich was gemacht werden soll.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Dies wollte ich jetzt eh machen, jedoch würde es mich interessieren, was man aus diesem Code herauslesen kann um zu beurteilen dies oder dies ist "besser" (nur für die Zukunft)
@dN!3L:
Math.Abs internals:
public static int Abs(int value)
{
if (value >= 0)
{
return value;
}
return AbsHelper(value);
}
und AbsHelper:
private static int AbsHelper(int value)
{
if (value == -2147483648)
{
throw new OverflowException(Environment.GetResourceString("Overflow_NegateTwosCompNum"));
}
return -value;
}
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Dann überrascht es mich noch mehr, dass beide "Methoden" fast gleich auf sind...
@dN!3L: Math.Abs internals: [...]
Nein, es geht hier um die double-Überladung (nicht int
!)
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Abs(double value);
-> Wie schon gesagt wird das (m.E.) nativ gemacht und ich würde schon von daher erwarten, dass es schneller ist.
Beste Grüße,
dN!3L
Hallo Quaneu,
Dies wollte ich jetzt eh machen, jedoch würde es mich interessieren, was man aus diesem Code herauslesen kann um zu beurteilen dies oder dies ist "besser" (nur für die Zukunft)
Im Verlauf der Diskussion hat sich der Kreis zum Maschinencode (CLR-Debugger) fast geschlossen. Mit diesem kannst du den Maschinencode ansehen und dort gibt es keinen Unterschied zwischen managed-Code und native-Code mehr.
Dort geht es nicht unbedingt darum was jeder Befehl macht sondern zB wenn Variante 1 nur 10 Befehle benötigt, Variante 2 jedoch 100 Befehle kann davon ausgegangen werden dass Variante 1 schneller ist.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Ach so. Ich dachte das dann auch Funktionsaufrufe und so eine größere Rolle spielen.
Vielen Dank für die Infos
@d3!NL:
Das seh ich im Release für die double Überladung:
double a = 3.0f;
0000009a fld dword ptr ds:[04161A50h]
000000a0 fstp qword ptr [ebp-44h]
double b = 7.0f;
000000a3 fld dword ptr ds:[04161A58h]
000000a9 fstp qword ptr [ebp-4Ch]
bool test1 = Math.Abs(a) > b;
000000ac fld qword ptr [ebp-44h]
000000af fabs
000000b1 fstp qword ptr [ebp+FFFFFF7Ch]
000000b7 fld qword ptr [ebp+FFFFFF7Ch]
000000bd fld qword ptr [ebp-4Ch]
000000c0 fcomip st,st(1)
000000c2 fstp st(0)
000000c4 jp 000000CA
000000c6 jb 000000CE
000000c8 jmp 000000CA
000000ca xor eax,eax
000000cc jmp 000000D3
000000ce mov eax,1
000000d3 mov dword ptr [ebp-50h],eax
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Hallo Quaneu,
Funktionsaufrufe sind auf Maschinencode-Ebene nur ein Jump zu einer anderen Speicheradresse.
Funktionsaufrufe spielen schon eine Rolle und daher gibts es Compiler-Optimierungen wie Inlining bei denen ein Methodenaufruf durch den Code der Methode ersetzt wird.
Aber:
Premature optimization is the root of all evil! (Donald E. Knuth).
Programmiere so dass der Code lesbar, verständlich, testbar ist. Das bringt insgesamt mehr als (unnötige) Optimierungen.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
@Quaneu: Hat dich das jetzt wirklich nur für double interessiert was schneller ist (s. Überschrift) oder allgemein der Ausdruck? Denn sonst müssten wir das für andere Fälle auch noch testen.
Quasi verschiedene Überladungen von Math.Abs(...) mitberücksichtigen.
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Programmiere so dass der Code lesbar, verständlich, testbar ist. Das bringt insgesamt mehr als (unnötige) Optimierungen.
Das möchte ich auch noch einmal unterstreichen. In den Tests hier wurde vielleicht herausgefunden, dass eine Variante schneller ist. Allerdings musst du auch den Kontext betrachten: Bei meinem Test ist Math.Abs fast doppelt so schnell wie die andere Varianten - allerdings: Bei 20.000.000 Wiederholungen war das ganze 200 Millisekunden schneller. Und die 200ms verlierst du an anderer Stelle mal ganz schnell z.B. durch IO.
Gruß,
dN!3L
Hallo,
ein schönes Schlusswort: The Sad Tragedy of Micro-Optimization Theater
It. Just. Doesn't. Matter!
m0rius
Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg
Hallo zusammen,
von mir noch ein Schlusswort auf der Meta-Ebene: Mir ist immer wieder unklar, warum solche Diskussionen - obwohl fast alle Beteiligten den Leitsatz "Premature optimization is the root of all evil!" kennen und auch in diesem Thread mehrfach darauf hingewiesen wurde - trotzdem noch in der Sache geführt werden. Wenn man "Premature optimization is the root of all evil!" beherzigt, muss man die Diskussion in der Sache überhaupt nicht führen und sollte das auch nicht, weil das verschwendete Energie ist. Man nimmt sinnvollerweise den lesbarsten Code und fertig. Selbst wenn man weiß, welcher Code schneller ist, ändert das ja überhaupt nichts!
herbivore