Von C++ nach Pascal

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Von C++ nach Pascal

Beitrag von Mathias »

Ich will folgenden C++ Code nach Pascal transferieren.

Code: Alles auswählen

void matrixMultiplication(float *matrixIn, // 4x4 matrix
float *vectorIn, // 4x1 vector
float *vectorOut) // 4x1 vector
{
// pointer row points to 16 elements array (beginning of the
// matrixIn array) containing column-major elements:
 // [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16]
float *row = matrixIn;
__asm
{
mov esi, vectorIn // load input address
mov edi, vectorOut // load output address
// pointer to the first 4 elements of the array:
mov edx, row

 
Soweit bin ich schon mal gekommen.
Nun zu Frage, ist dies der richtige weg, auch was die var im Procedure-Kopf anbelangt ?
Und wie muss ich das float *row = matrixIn; umsetzen ?

Code: Alles auswählen

  procedure matrixMultiplication(var matrixIn: TMatrix; var vectorIn, vectorOut: TVector4f);
    {$asmmode intel}
  begin
    asm
             Mov     Esi, vectorIn // load input address
             Mov     Edi, vectorOut // load output address
             // pointer to the first 4 elements of the array:
             Mov     Edx, row


Der komplette C++-Code befindet sich hier: http://www-cs.ccny.cuny.edu/~gertner/Cs ... tion_4.pdf
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Von C++ nach Pascal

Beitrag von Warf »

Was sind denn überhaupt die typen TMatrix und TVector4f? Denn der Code arbeitet auf Pointern, wenn die beiden typen Statische Arrays oder Records sind wirst du mit deinem Ansatz nicht glücklich denn mit:

Code: Alles auswählen

Mov     Esi, vectorIn

würde dann der Inhalt statt die Addresse in ESI geladen werden, was spätetestens bei

Code: Alles auswählen

movups xmm0, [esi]

knallt.

Wenn es Dynamische Arrays sind dann kannst du den Code praktisch direkt übernehmen:

Code: Alles auswählen

Row: PSingle; // vordefiniert???
...
Row := PSingle(matrixIn)

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Von C++ nach Pascal

Beitrag von Mathias »

Danke erst mal.

Auf die Idee mit dem PSingle bin ich unterdessen auch gekommen. Auch habe ich die 64Bit-Register angepasst.

Nun sieht mein Kopf so aus. Immerhin knallt es nicht mehr.

Code: Alles auswählen

  procedure matrixMultiplication(matrixIn, vectorIn, vectorOut: PSingle);
  var
    i: integer;
    row: PSingle;
 
    {$asmmode intel}
  begin
    row := matrixIn;
    asm
             Mov     Rsi, vectorIn // load input address
             Mov     Rdi, vectorOut // load output address
             // pointer to the first 4 elements of the array:
             Mov     Rdx, row
             // Move Unaligned Parallel Scalars
             // load the registers with matrix values:
             Movups  Xmm4, [Rdx] // xmm4 contains 1,5, 9,13
             Movups  Xmm5, [Rdx+$10] // +16 (4 bytes x 4 elements)


Der Aufruf sieht folgendermassen aus:

Code: Alles auswählen

type
  glFloat = Single;  // aus dglOpenGL.pas
 
  TVector4f = array[0..3] of GLfloat;
  Tmat4x4 = array[0..3] of TVector4f;
  TMatrix = Tmat4x4;
 
var
    Matrix: TMatrix
    v0, vv: TVector4f;
 
begin
  vv := v0;
  matrixMultiplication(@Matrix, @vv, @v0);
 


Das Ergebnis sie aus wie im Anhang. Eigentlich sollte es ein Würfel sein.
Dateianhänge
Bildschirmfoto vom 2018-06-21 20-17-00.png
Bildschirmfoto vom 2018-06-21 20-17-00.png (6.74 KiB) 1218 mal betrachtet
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Von C++ nach Pascal

Beitrag von Warf »

Ich habe grad mal nachgeschaut, zumindest Delphi unterstützt den @ Operoator im ASM, also könnte eventuell auch das gehen:

Code: Alles auswählen

  procedure matrixMultiplication(matrixIn: TMatrix; vectorIn: TVector4f; out vectorOut: TVector4f ); // muss ja nur eins output sein
  var
    i: integer;
    row: PSingle;
 
    {$asmmode intel}
  begin
    row := PSingle(@MatrixIn);
    asm
             Mov     Rsi, @vectorIn // load input address
             Mov     Rdi, @vectorOut // load output address
             // pointer to the first 4 elements of the array:
             Mov     Rdx, row
             // Move Unaligned Parallel Scalars
             // load the registers with matrix values:
             Movups  Xmm4, [Rdx] // xmm4 contains 1,5, 9,13
             Movups  Xmm5, [Rdx+$10] // +16 (4 bytes x 4 elements)


Ich mag es nicht wenn Funktionsaufrufe Pointer brauchen (wenn es sich vermeiden lässt)

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Von C++ nach Pascal

Beitrag von Mathias »

Das Ergebnis sie aus wie im Anhang. Eigentlich sollte es ein Würfel sein.

Der Fehler war hier, und somit kein Pointer-Fehler. Irgendwie habe die die Werte in der Matrix anders rum übergeben.
Da sieht man wieder, das es vieles im INet, welches Fehler enthält.

Code: Alles auswählen

             Movups  Xmm4, [Rdx+$30] // xmm4 contains 1,5, 9,13
             Movups  Xmm5, [Rdx+$20] // +16 (4 bytes x 4 elements)
             // xmm5 contains 2,6,10,14
             Movups  Xmm6, [Rdx+$10] // +32 (8 bytes x 4 elements)
             // xmm6 contains 3,7,11,15
             Movups  Xmm7, [Rdx+$00] // +48 (12 bytes x 4 


Den Kopf habe ich noch ein wenig abgeändert und somit kann ich voll auf Pointer verzichten. @ ist nicht nötig.

Code: Alles auswählen

  procedure matrixMultiplication(const matrixIn: TMatrix; const vectorIn: TVector4f; out vectorOut: TVector4f);
    {$asmmode intel}
  begin
    asm
             Mov     Rsi, vectorIn // load input address
             Mov     Rdi, vectorOut // load output address
             // pointer to the first 4 elements of the array:
             Mov     Rdx, matrixIn;


Auch das Haupt-Programm ist Pointer frei.

Code: Alles auswählen

begin
  matrixMultiplication(Matrix, v0, v0);
  matrixMultiplication(Matrix, v1, v1);
  matrixMultiplication(Matrix, v2, v2);

Assembler einfacher geworden gegenüber eines 8088er. Man muss sich nicht mehr mit Offset und Segment rum schlagen. :wink:
Mit diesen MMX-Funktionen kann man die Matrizen und Vektor-Berechnungen von OpenGL ein wenig beschleunigen. :wink:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Von C++ nach Pascal

Beitrag von Mathias »

Ich habe grad mal nachgeschaut, zumindest Delphi unterstützt den @ Operoator im ASM, also könnte eventuell auch das gehen:

Es geht sogar noch eleganter, ohne Umwege über ein Register.

Code: Alles auswählen

function VectorMultiplySSE(const mat: TMatrix; const vec: TVector4f): TVector4f; assembler;
  {$asmmode intel}
asm
         Movups  Xmm4, [mat+$30] // xmm4 contains 1,5, 9,13
         Movups  Xmm5, [mat+$20] // +16 (4 bytes x 4 elements)

Oder ist dies etwa langsamer als folgender Code ?

Code: Alles auswählen

function VectorMultiplySSE(const mat: TMatrix; const vec: TVector4f): TVector4f; assembler;
  {$asmmode intel}
asm
         Movups  Xmm4, [rsi+$30] // xmm4 contains 1,5, 9,13
         Movups  Xmm5, [mat+$20] // +16 (4 bytes x 4 elements) 

Der kompilierte Code ist in beiden Varianten exakt gleich lang.

Ich habe noch im Debugger nachgeguckt, dort wird mat auf RSI übersetzt.

Code: Alles auswählen

unit1.pas:84                              Movups  Xmm4, [rsi+$30] // xmm4 contains 1,5, 9,13
000000000045FAB4 0f106630                 movups 0x30(%rsi),%xmm4
unit1.pas:85                              Movups  Xmm5, [mat+$20] // +16 (4 bytes x 4 elements)
000000000045FAB8 0f106e20                 movups 0x20(%rsi),%xmm5
unit1.pas:87                              Movups  Xmm6, [rsi+$10] // +32 (8 bytes x 4 elements)
000000000045FABC 0f107610                 movups 0x10(%rsi),%xmm6
unit1.pas:89                              Movups  Xmm7, [mat+$00] // +48 (12 bytes x 4
 

Der Bezeichnername finde ich eleganter, der kennt man garantiert, aber was für ein Register der Kompiler nimmt ist eine Frage.

Mein nächster Schritt wird sein, die SSE-Register zu verstehen, die sind voll Neuland für mich. :wink:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten