Autor |
Beitrag |
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 29.08.04 19:52
Ich hab gestern mal ein seltsames Phänomen festgestellt, dass ich mir nicht so richtig erklären kann:
Ich hab zwei Routinen geschrieben, die beide das gleiche machen: Prüfen ob eine Zahl prim ist. Zur
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153:
| Function IsPrime(Number: DWORD): Boolean; Asm PUSH EBX PUSH ESI XOR EBX, EBX MOV ESI, EAX CMP ESI, $00000002 JB @@IsPrime_NoPrime JZ @@IsPrime_Prime
TEST ESI, $00000001 JZ @@IsPrime_NoPrime MOV ECX, $03 JMP @@IsPrime_CheckIfLess
@@IsPrime_Loop: MOV EAX, ESI XOR EDX, EDX DIV ECX TEST EDX, EDX JZ @@IsPrime_NoPrime
ADD ECX, $02
@@IsPrime_CheckIfLess: MOV EAX, ECX MUL ECX CMP EAX, ESI JA @@IsPrime_Loop JZ @@IsPrime_NoPrime @@IsPrime_Prime: MOV BL, $01 @@IsPrime_NoPrime: MOV EAX, EBX @@Ret: POP ESI POP EBX End;
Function IsPrime2(Number: DWORD): Boolean; Var Teiler: DWORD; Begin Result := False; If Number < 2 Then Exit Else If Number = 2 Then Begin Result := True; Exit; End;
If Number Mod 2 = 0 Then Exit;
Teiler := 3; While Teiler * Teiler <= Number Do Begin If Teiler * Teiler <= Number Then Exit;
If Number Mod Teiler = 0 Then Exit;
Inc(Teiler, 2); End;
Result := True; End;
Procedure TForm1.TestClick(Sender: TObject); Var T1, T2, T3, T4, T5: Int64; X, Y, Z: DWORD; B: Boolean; Const NumTests = 500; ScaleBy = 50000; ScaleByY = 25; MaxNumber = 50000000; PerPixel = $7F; Begin QueryPerformanceFrequency(T1); Image1.Height := 500; Image1.Width := 1 + MaxNumber Div ScaleBy; Image1.Picture.Bitmap.Height := Image1.Height; Image1.Picture.Bitmap.Width := Image1.Width; Image1.Picture.Bitmap.Canvas.Brush.Color := clBlack; Image1.Picture.Bitmap.Canvas.FillRect(Image1.Picture.Bitmap.Canvas.ClipRect); QueryPerformanceCounter(T2); QueryPerformanceCounter(T4); T4 := T4 - T2; With Image1.Picture.Bitmap, Canvas Do Begin T5 := 0; For Y := 1 To MaxNumber Do Begin B := True; QueryPerformanceCounter(T2); For X := 1 To NumTests Do B := IsPrime(Y) And B; QueryPerformanceCounter(T3);
T3 := T3 - T2 - T4; If T3 < NumTests Then T3 := NumTests; T5 := T5 + T3;
X := Height - Round(ScaleByY * Ln(T3 / NumTests)) - 1; Z := Pixels[Y Div ScaleBy, X]; If Z and $000000FF < $100-PerPixel Then Z := Z +PerPixel; Pixels[Y Div ScaleBy, X] := Z;
If Y Mod ScaleBy = 0 Then Begin Update; End; End; T5 := 0; For Y := 1 To MaxNumber Do Begin B := True; QueryPerformanceCounter(T2); For X := 1 To NumTests Do B := IsPrime2(Y) And B; QueryPerformanceCounter(T3); T3 := T3 - T2 - T4; If T3 < NumTests Then T3 := NumTests; T5 := T5 + T3;
X := Height - Round(ScaleByY * Ln(T3 / NumTests)) - 1; Z := Pixels[Y Div ScaleBy, X]; If Z and $0000FF00 < $100 * ($100-PerPixel) Then Z := Z +$100*PerPixel; Pixels[Y Div ScaleBy, X] := Z;
If Y Mod ScaleBy = 0 Then Begin Update; End; End; End; End; |
Dabei hab ich festgestellt, dass wenn man sich jede einzelne Messung anzeigen lässt man den Eindruck hat, als ob ASM LANGSAMER wäre als die Delphi-Prozedur (aktueller Quelltext). Entfernt man aber die Kommentare über den Beiden "Update;"-Zeilen und lässt sich den Durchschnitt anzeigen, so ist wie zu erwarten die ASM Routine schneller. Auch fällt auf, dass bei der Einzelpunkt-Anzeige die ASM-Routine wesentlich breiter streut, OBWOHL sie bis auf 2 Befehle weniger genau den gleichen Quelltext beinhaltet.
Hat jemand eine Idee, woran dies liegen könnte?
Achso: NumTests evtl. Anpassen, da es sonst zu Fehlern wegen 0-Taktzyklen-Messungen kommen kann.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
.Chef
      
Beiträge: 1112
|
Verfasst: Mo 30.08.04 10:34
Ein Optimierungsproblem, und ich habe grad kein Delphi zur Hand.
Schau ich mir heut abend aber mal genauer an, wenn ich wieder an meinem Rechner sitze.
Die Streuung lässt sich vielleicht mit unterschiedlicher Subroutinenverwaltung erklären. Es könnte ja sein, dass du das weniger optimal gelöst hast als der Compiler.
_________________ Die Antworten auf die 5 häufigsten Fragen:
1. Copy(), Pos(), Length() --- 2. DoubleBuffered:=True; --- 3. Application.ProcessMessages bzw. TThread --- 4. ShellExecute() --- 5. Keine Vergleiche von Real-Typen mit "="!
|
|
BenBE 
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 30.08.04 21:00
Naja, kann ich mir eher weniger Vorstellen, da der Source nahezu 1:1 vom CPU-Window übernommen wurde und dann zwei Dinge noch optimiert wurden:
1. Ein CMP-Befehl bei der Bereichsprüfung der Schleife entfernt
2. der JZ Befehl aus Zeile 31 wurde hinter den JA-Befehl gesetzt, um die Schleife schneller zu machen.
Das einzige Problem könnte ein winziger Bug in Zeile 29 gewesen sein, wo es CMP ESI, EAX anstatt CMP EAX, ESI heißén muss.
Hab aber andere Unregelmäßigkeiten entdeckt:
1. Wenn man mehrmals (auch mit REALTIME_CLASS) QueryPerformanceCounter hintereinander aufruft, so können sich die Werte um bis zu 1% Unterscheiden.
2. Bei sehr geringen Messzeiten ist die Messungenauigkeit selbst bei 1,4 Mrd Messzyklen (auf meinem AMD) sehr groß (Schwankungen beim Testprogramm um nahezu 5%! sowie eine breite Streuung. Die Messung der gleichen Routine kann sehr unterschiedliche Ergebnisse liefern (auch REALTIME_CLASS).
3. Auf mancher Hardware liefert die Messroutine IMMER 0 zurück, selbst wenn man 500000 Tests (NumTests für IP4 bei 1,6 GHz und nach meinen Messungen ca. 400\1400013000 Messzyklen) durchführen lässt und die Messfrequenz mit 3,5 Mio von QueryPerformanceFrequency angegeben wird.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
|