AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems
Thema durchsuchen
Ansicht
Themen-Optionen

Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

Ein Thema von Alex_ITA01 · begonnen am 16. Mai 2023 · letzter Beitrag vom 17. Mai 2023
Antwort Antwort
Seite 1 von 2  1 2      
Alex_ITA01

Registriert seit: 22. Sep 2003
1.115 Beiträge
 
Delphi 12 Athens
 
#1

Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 12:35
Hallo zusammen,
ich stehe aktuell auf dem Schlauch.
Die Sortierfunktion von TList gibt mir ein anderes Ergebnis bei einem 32bit Compilat wie bei einem 64bit Compilat.
Könnt ihr mir sagen warum?

Delphi 11, Windows 10 64bit

Delphi-Quellcode:
unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Math;

type
  PMyStruct = ^TMyStruct;
  TMyStruct = packed record
    Value1 : LongInt;
    Value2 : Double;
    Value3 : Byte;

    function GetDataStr: String;
  end;

type
  TForm3 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure TForm3.FormCreate(Sender: TObject);
var
  aList : TList;
  aData : PMyStruct;
  i: Integer;

  function CompareItems(const Item1,Item2: PMyStruct): Integer;
  begin
    Result := Math.CompareValue(Item1^.Value1, Item2^.Value1);
    if Result <> 0 then
      exit;

    Result := Math.CompareValue(Item2^.Value2, Item1^.Value2);
    if Result <> 0 then
      exit;

    Result := Math.CompareValue(Item1^.Value3, Item2^.Value3);
  end;

begin
  aList := TList.Create;
  try
    for i := 0 to 5 do
    begin
      New(aData);
      aData^.Value1 := 1429078;
      aData^.Value2 := 6.220;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    for i := 0 to 5 do
    begin
      New(aData);
      aData^.Value1 := 1429079;
      aData^.Value2 := 6.220;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    for i := 0 to 5 do
    begin
      New(aData);
      aData^.Value1 := 1429080;
      aData^.Value2 := 6.220;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    for i := 0 to 0 do
    begin
      New(aData);
      aData^.Value1 := 1429081;
      aData^.Value2 := 6.220;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    for i := 0 to 1 do
    begin
      New(aData);
      aData^.Value1 := 1429081;
      aData^.Value2 := 5.810;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    for i := 0 to 4 do
    begin
      New(aData);
      aData^.Value1 := 1429082;
      aData^.Value2 := 5.810;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    for i := 0 to 5 do
    begin
      New(aData);
      aData^.Value1 := 1429083;
      aData^.Value2 := 5.810;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    for i := 0 to 5 do
    begin
      New(aData);
      aData^.Value1 := 1429084;
      aData^.Value2 := 5.810;
      aData^.Value3 := i;

      aList.Add(aData);
    end;

    Memo1.Clear;
    Memo1.Lines.Add('VOR Sortierung');
    for i := 0 to aList.Count - 1 do
    begin
      aData := aList.Items[i];

      Memo1.Lines.Add('(' + i.ToString +') - aData: ' + aData^.GetDataStr);
    end;

    aList.Sort(@CompareItems);

    Memo2.Clear;
    Memo2.Lines.Add('NACH Sortierung');
    for i := 0 to aList.Count - 1 do
    begin
      aData := aList.Items[i];

      Memo2.Lines.Add('(' + i.ToString +') - aData: ' + aData^.GetDataStr);
    end;

  finally
    FreeAndNil(aList);
  end;
end;

{ TMyStruct }

function TMyStruct.GetDataStr: String;
begin
  Result := Value1.ToString + '/' + FloatToStrF(Value2, ffFixed, 15, 3) + '/' + Value3.ToString;
end;

end.
Ergebnis nach Sortierung 32bit:
NACH Sortierung
(0) - aData: 1429078/6,220/0
(1) - aData: 1429078/6,220/1
(2) - aData: 1429078/6,220/2
(3) - aData: 1429078/6,220/3
(4) - aData: 1429078/6,220/4
(5) - aData: 1429078/6,220/5
(6) - aData: 1429079/6,220/0
(7) - aData: 1429079/6,220/1
(8) - aData: 1429079/6,220/2
(9) - aData: 1429079/6,220/3
(10) - aData: 1429079/6,220/4
(11) - aData: 1429079/6,220/5
(12) - aData: 1429080/6,220/0
(13) - aData: 1429080/6,220/1
(14) - aData: 1429080/6,220/2
(15) - aData: 1429080/6,220/3
(16) - aData: 1429080/6,220/4
(17) - aData: 1429080/6,220/5
(18) - aData: 1429081/6,220/0
(19) - aData: 1429081/5,810/0
(20) - aData: 1429081/5,810/1
(21) - aData: 1429082/5,810/0
(22) - aData: 1429082/5,810/1
(23) - aData: 1429082/5,810/2
(24) - aData: 1429082/5,810/3
(25) - aData: 1429082/5,810/4
(26) - aData: 1429083/5,810/0
(27) - aData: 1429083/5,810/1
(28) - aData: 1429083/5,810/2
(29) - aData: 1429083/5,810/3
(30) - aData: 1429083/5,810/4
(31) - aData: 1429083/5,810/5
(32) - aData: 1429084/5,810/0
(33) - aData: 1429084/5,810/1
(34) - aData: 1429084/5,810/2
(35) - aData: 1429084/5,810/3
(36) - aData: 1429084/5,810/4
(37) - aData: 1429084/5,810/5

Ergebnis nach Sortierung 64bit:
NACH Sortierung
(0) - aData: 1429082/5,810/3
(1) - aData: 1429082/5,810/4
(2) - aData: 1429083/5,810/0
(3) - aData: 1429083/5,810/1
(4) - aData: 1429082/5,810/2
(5) - aData: 1429081/5,810/0
(6) - aData: 1429081/5,810/1
(7) - aData: 1429082/5,810/0
(8) - aData: 1429082/5,810/1
(9) - aData: 1429083/5,810/2
(10) - aData: 1429084/5,810/2
(11) - aData: 1429084/5,810/3
(12) - aData: 1429084/5,810/4
(13) - aData: 1429084/5,810/5
(14) - aData: 1429084/5,810/1
(15) - aData: 1429083/5,810/3
(16) - aData: 1429083/5,810/4
(17) - aData: 1429083/5,810/5
(18) - aData: 1429084/5,810/0
(19) - aData: 1429078/6,220/5
(20) - aData: 1429079/6,220/0
(21) - aData: 1429079/6,220/1
(22) - aData: 1429079/6,220/2
(23) - aData: 1429078/6,220/4
(24) - aData: 1429078/6,220/0
(25) - aData: 1429078/6,220/1
(26) - aData: 1429078/6,220/2
(27) - aData: 1429078/6,220/3
(28) - aData: 1429079/6,220/3
(29) - aData: 1429080/6,220/3
(30) - aData: 1429080/6,220/4
(31) - aData: 1429080/6,220/5
(32) - aData: 1429081/6,220/0
(33) - aData: 1429080/6,220/2
(34) - aData: 1429079/6,220/4
(35) - aData: 1429079/6,220/5
(36) - aData: 1429080/6,220/0
(37) - aData: 1429080/6,220/1

Anmerkung:
Erste Durchlauf von "Result := Math.CompareValue(Item1^.Value1, Item2^.Value1);" unter 32bit ist
Item1^.Value1 -> 1429078
und
Item2^.Value1 -> 1429081

Erste Durchlauf von "Result := Math.CompareValue(Item1^.Value1, Item2^.Value1);" unter 64bit ist
Item1^.Value1 -> 1429081
und
Item2^.Value1 -> 1429081

Das heißt, die Liste selber ruft meine Comparefunktion schon unterschiedlich auf...
Let's fetz sprach der Frosch und sprang in den Mixer

Geändert von Alex_ITA01 (16. Mai 2023 um 12:40 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.009 Beiträge
 
Delphi 12 Athens
 
#2

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 12:51
Du kannst eine lokale Funktion nicht als Pointer übergeben. Das geht spätestens bei 64-Bit wegen der anderen Aufrufkonventionen schief. Das @ beim Sort ist schon ein markanter Hinweis für eine Fehlerursache.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Alex_ITA01

Registriert seit: 22. Sep 2003
1.115 Beiträge
 
Delphi 12 Athens
 
#3

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 12:57
ok, wie muss es dann heißen?
Selbst in der Hilfe steht es doch so... Woher soll man das dann wissen
Miniaturansicht angehängter Grafiken
2023.05.16-13_55_24-001.png  
Let's fetz sprach der Frosch und sprang in den Mixer
  Mit Zitat antworten Zitat
Andreas13

Registriert seit: 14. Okt 2006
Ort: Nürnberg
711 Beiträge
 
Delphi XE5 Professional
 
#4

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 13:23
Wahrscheinlich klappt es, wenn Du die function CompareItems(..) direkt in TForm3 = class(TForm) verschiebst.
Grüße, Andreas
Wenn man seinem Nächsten einen steilen Berg hinaufhilft, kommt man selbst dem Gipfel näher. (John C. Cornelius)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.009 Beiträge
 
Delphi 12 Athens
 
#5

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 13:26
Entweder so:
Delphi-Quellcode:
type
  PMyStruct = ^TMyStruct;
  TMyStruct = packed record
    Value1 : LongInt;
    Value2 : Double;
    Value3 : Byte;

    class function Compare(const A, B: TMyStruct): TValueRelationship; static;
    function GetDataStr: String;
  end;

class function TMyStruct.Compare(const A, B: TMyStruct): TValueRelationship;
begin
  Result := CompareValue(A.Value1, B.Value1);
  if Result <> 0 then
    exit;

  Result := CompareValue(A.Value2, B.Value2);
  if Result <> 0 then
    exit;

  Result := CompareValue(A.Value3, B.Value3);
end;

function CompareItems(PItem1,PItem2: Pointer): Integer;
begin
  Result := TMyStruct.Compare(PMyStruct(PItem1)^, PMyStruct(PItem2)^);
end;

...
    aList.Sort(CompareItems);
...
Oder gleich mit einer generischen Liste (Das spart übrigens auch die Freigabe der Records):
Delphi-Quellcode:
type
  TMyStruct = packed record
    Value1 : LongInt;
    Value2 : Double;
    Value3 : Byte;

    constructor Create(const AValue1: LongInt; const AValue2: Double; const AValue3: Byte);
    class function Compare(const A, B: TMyStruct): Integer; static;
    function GetDataStr: String;
  end;

  TMyStructList = class(TList<TMyStruct>)
  public
    constructor Create;
  end;

constructor TMyStruct.Create(const AValue1: LongInt; const AValue2: Double; const AValue3: Byte);
begin
  Value1 := AValue1;
  Value2 := AValue2;
  Value3 := AValue3;
end;

class function TMyStruct.Compare(const A, B: TMyStruct): Integer;
begin
  Result := CompareValue(A.Value1, B.Value1);
  if Result <> 0 then
    exit;

  Result := CompareValue(A.Value2, B.Value2);
  if Result <> 0 then
    exit;

  Result := CompareValue(A.Value3, B.Value3);
end;

function TMyStruct.GetDataStr: String;
begin
  Result := Value1.ToString + '/' + FloatToStrF(Value2, ffFixed, 15, 3) + '/' + Value3.ToString;
end;

constructor TMyStructList.Create;
begin
  inherited Create(TComparer<TMyStruct>.Construct(TMyStruct.Compare));
end;

...
  var aList := TMyStructList.Create;
  try
    for var i := 0 to 5 do
      aList.Add(TMyStruct.Create(1429078, 6.220, i));

    for var i := 0 to 5 do
      aList.Add(TMyStruct.Create(1429079,6.220, i));

    for var i := 0 to 5 do
      aList.Add(TMyStruct.Create(1429080,6.220, i));

    for var i := 0 to 0 do
      aList.Add(TMyStruct.Create(1429081,6.220, i));

    for var i := 0 to 1 do
      aList.Add(TMyStruct.Create(1429081,5.810, i));

    for var i := 0 to 4 do
      aList.Add(TMyStruct.Create(1429082,5.810, i));

    for var i := 0 to 5 do
      aList.Add(TMyStruct.Create(1429083,5.810, i));

    for var i := 0 to 5 do
      aList.Add(TMyStruct.Create(1429084,5.810, i));

    Memo1.Clear;
    Memo1.Lines.Add('VOR Sortierung');
    for var i := 0 to aList.Count - 1 do
    begin
      var aData := aList.Items[i];

      Memo1.Lines.Add('(' + i.ToString +') - aData: ' + aData.GetDataStr);
    end;

    aList.Sort;

    Memo2.Clear;
    Memo2.Lines.Add('NACH Sortierung');
    for var i := 0 to aList.Count - 1 do
    begin
      var aData := aList.Items[i];

      Memo2.Lines.Add('(' + i.ToString +') - aData: ' + aData.GetDataStr);
    end;

  finally
    FreeAndNil(aList);
  end;
...
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Alex_ITA01

Registriert seit: 22. Sep 2003
1.115 Beiträge
 
Delphi 12 Athens
 
#6

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 13:50
Danke Uwe, habe Variante 1 genommen.
Auf Generic konnte ich nicht so leicht umbauen aber Variante 1 funktioniert. Danke.

Was lernen wir daraus? Nicht auf die Hilfe hören
Let's fetz sprach der Frosch und sprang in den Mixer
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.009 Beiträge
 
Delphi 12 Athens
 
#7

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 13:58
Was lernen wir daraus? Nicht auf die Hilfe hören
Vermutlich stammt die Hilfe noch von Delphi 1 oder so.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.008 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#8

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 22:36
Mir war so, als ob ich dieses Problem schonmal gesehen habe und jupp, hier isses.

Was lernen wir daraus? Nicht auf die Hilfe hören
Ich seh in der Hilfe keine verschachtelte Routine...

Habs mal reported
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie (16. Mai 2023 um 22:47 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.142 Beiträge
 
Delphi 12 Athens
 
#9

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 16. Mai 2023, 22:58
Methoden und diese Inline, sowie die Generischen haben einen "unsichtbaren" ersten Self-Parameter.

Somit stimmt die Signatur dann nicht, im Vergleich mit einer "reinen" Prozedur.
Bei Methoden und Klassenmethoden kann man das mit Static beheben, aber für Untermethoden leider nicht.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.008 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#10

AW: Unterschiedliche Ergebnisse TList.Sort 32bit / 64bit CompareItems

  Alt 17. Mai 2023, 00:04
Methoden und diese Inline, sowie die Generischen haben einen "unsichtbaren" ersten Self-Parameter.

Somit stimmt die Signatur dann nicht, im Vergleich mit einer "reinen" Prozedur.
Bei Methoden und Klassenmethoden kann man das mit Static beheben, aber für Untermethoden leider nicht.
Ja, sie haben einen unsichtbaren ersten Parameter (unter 32bit nur wenn etwas aus dem äußere Scope referenziert wird, unter 64bit immer).
Dieser hat aber nix mit dem Self-Parameter wie bei Methoden (entweder Referenz zur Instanz oder zu Klasse) zu tun, sondern hier wird die Addresse zum umgebenden Stackframe übergeben.
Darüber wird der Zugriff auf die Variablen außerhalb der Routine realisiert.

Wie David auf die von mir verlinkten Stackoverflow richtig erklärt, gibt es diesen unter Win64 immer, egal ob man auf den umgebenden Scope zugreift oder nicht.
Unter Win32 gibt es diesen nur wenn man auf den umgebenden Scope zugreift - deshalb funktioniert es dort.

Unter Win64 werden die ersten vier Parameter in den Registern rcx, rdx, r8 und r9 übergeben. Also sollten Item1 und Item2 in rcx und rdx sein, da wirds auch durch den Aufruf aus Sort heraus übergeben.
Dadurch, dass die Implementierung aber nested ist, nimmt der Compiler aber an, dass diese in rdx und r8 sind, denn in rcx erwartet er den Stackzeiger, wenn die Routine lokal aufgerufen wird.

Das kann man sehr einfach auch überprüfen, wenn man einfach mal in das FormCreate CompareItems(nil, nil); reinschreibt, dort einen Stoppunkt setzt und sich das Disassembly anschaut:

Delphi-Quellcode:
mov rcx,rbp
xor edx,edx
xor r8,r8
call CompareItems
Wie man sieht, wird rbp (Frame pointer) in rcx übergeben, wie ich zuvor beschrieben habe. Verschiebe ich CompareItems nach außerhalb schauts so aus:

Delphi-Quellcode:
xor ecx,ecx
xor edx,edx
call CompareItems
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie (17. Mai 2023 um 00:11 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:09 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz