Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2005-03

Floating Point Performance on Intel

5 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Raw Magick DOT COM

Hi All,

My name is Peter Dove, I am new to FPC and Lazarus. I come from a mainly Delphi background but I use C, C++ and assembler as needed to improve performance on the imagining app we are working on.

Like Delphi, FPC has a poor floating point optimisation situation in comparison to similar compiles in C. For instance the following code in Pascal

     A := 0;
     B := 0.9;
     For X := 0 to 10000000 do
     begin
          A := A + X;
          A := A * B;
     end;

Takes some 220ms to perform. The major problem with the performance is the poor loop optimisation and register usage, also with wasted push and pulls from memory. Below is the result from the assembler output from FPC - all optimisations were enabled..

# Var A located at ebp-4
# Var B located at ebp-8
# Var X located at ebp-12

//A + B are set up before here - its the loop thats interrsting

# [44] For X := 0 to 10000000 do
movl $0,-12(%ebp)
decl -12(%ebp)
.balign 4
.L31:
incl -12(%ebp)
# [46] A := A + X;
flds -4(%ebp)
fildl -12(%ebp)
faddp %st,%st(1)
fstps -4(%ebp)
# [47] A := A * B;
flds -8(%ebp)
fmuls -4(%ebp)
fstps -4(%ebp)
cmpl $10000000,-12(%ebp)
jl .L31

My comments on this are that

a) The loop counter is basically a comparison against a memory area = slow
b) There are some unnessary loads from memory occuring = slow

The above code takes about 210ms to perform on my machine. Below is my own assembler which takes about 100ms ( apologies it is in a slightly different format )

asm
   mov eax, 0; //Set up loop counter
   @StartOfLoop:
   mov dword ptr[x], eax; // Move its value into X ( on stack )
   FILD dword ptr[x]; //Load into floating point
   FADD dword ptr[A]; // Add A ( on Stack ) to it
   FMUL dword ptr[B]; //Multiply by B ( on Stack )
   FSTP dword ptr[A]; // Pop into A
   add eax, 1; //Inc loop counter
   cmp eax, 10000000; // Test Jump condition
   jl @StartOfLoop;
end;

My question is, what needs to be done to the compiler to make it optimise as well as C compilers, or perhaps I am missing some compiler switches.

Florian Klaempfl
to Raw Magick DOT COM
Raw Magick DOT COM wrote 22 lines
Hi All, My name is Peter Dove, I am new to FPC and Lazarus. I come from a mainly Delphi background but I use C, C++ and assembler as needed to improve performance on the imagining app we are working on. Like Delphi, FPC has a poor floating point optimisation situation in comparison to similar compiles in C. For instance the following code in Pascal A := 0; B := 0.9; For X := 0 to 10000000 do begin A := A + X; A := A * B; end; Takes some 220ms to perform. The major problem with the performance is the poor loop optimisation and register usage, also with wasted push and pulls from memory. Below is the result from the assembler output from FPC - all optimisations were enabled..

The problem with such optimizations is that usually the compiler knows too little about a program so such optimizations apply to seldom and aren't worse the affort to be implemented. E.g. your assembler assumes that a:=a*b can't throw an exception, but it can and the compiler isn't allowed to assume that it doesn't.

Show quoted text 29 lines
# Var A located at ebp-4 # Var B located at ebp-8 # Var X located at ebp-12 //A + B are set up before here - its the loop thats interrsting # [44] For X := 0 to 10000000 do movl $0,-12(%ebp) decl -12(%ebp) .balign 4 .L31: incl -12(%ebp) # [46] A := A + X; flds -4(%ebp) fildl -12(%ebp) faddp %st,%st(1) fstps -4(%ebp) # [47] A := A * B; flds -8(%ebp) fmuls -4(%ebp) fstps -4(%ebp) cmpl $10000000,-12(%ebp) jl .L31 My comments on this are that a) The loop counter is basically a comparison against a memory area = slow

Well, you need to write the counter to the memory as well, so this shouldn't count much.

Show quoted text 20 lines
b) There are some unnessary loads from memory occuring = slow The above code takes about 210ms to perform on my machine. Below is my own assembler which takes about 100ms ( apologies it is in a slightly different format ) asm mov eax, 0; //Set up loop counter @StartOfLoop: mov dword ptr[x], eax; // Move its value into X ( on stack ) FILD dword ptr[x]; //Load into floating point FADD dword ptr[A]; // Add A ( on Stack ) to it FMUL dword ptr[B]; //Multiply by B ( on Stack ) FSTP dword ptr[A]; // Pop into A add eax, 1; //Inc loop counter cmp eax, 10000000; // Test Jump condition jl @StartOfLoop; end; My question is, what needs to be done to the compiler to make it

The compiler needs a proper lifetime analysis of expressions.

optimise as well as C compilers,

See above, this is often not possible in pascal.

or perhaps I am missing some compiler switches.
Jan Ruzicka
to Raw Magick DOT COM: My name is Peter Dove, I am new to FPC and Lazarus. I come from a mainly Delphi…
Raw Magick DOT COM wrote 8 lines
A := 0; B := 0.9; For X := 0 to 10000000 do begin A := A + X; A := A * B; end;
Show quoted text 46 lines
# Var A located at ebp-4 # Var B located at ebp-8 # Var X located at ebp-12 //A + B are set up before here - its the loop thats interrsting # [44] For X := 0 to 10000000 do movl $0,-12(%ebp) decl -12(%ebp) .balign 4 .L31: incl -12(%ebp) # [46] A := A + X; flds -4(%ebp) fildl -12(%ebp) faddp %st,%st(1) fstps -4(%ebp) # [47] A := A * B; flds -8(%ebp) fmuls -4(%ebp) fstps -4(%ebp) cmpl $10000000,-12(%ebp) jl .L31 The above code takes about 210ms to perform on my machine. Below is my own assembler which takes about 100ms ( apologies it is in a slightly different format ) asm mov eax, 0; //Set up loop counter @StartOfLoop: mov dword ptr[x], eax; // Move its value into X ( on stack ) FILD dword ptr[x]; //Load into floating point FADD dword ptr[A]; // Add A ( on Stack ) to it FMUL dword ptr[B]; //Multiply by B ( on Stack ) FSTP dword ptr[A]; // Pop into A add eax, 1; //Inc loop counter cmp eax, 10000000; // Test Jump condition jl @StartOfLoop; end; My question is, what needs to be done to the compiler to make it optimise as well as C compilers, or perhaps I am missing some compiler switches.

I expect you are using x86 family (may be even IA-32 architecture). what about writing your code as:

      A := 0;
      B := 0.9;
      For X := 10000000 downto 0 do
      begin
           A := A + X;
           A := A * B;
      end;
asm
    mov ecx, 10000000; //Set up loop counter
    @StartOfLoop:
    mov dword ptr[x], ecx; // Move its value into X ( on stack )
    FILD dword ptr[x]; //Load into floating point
    FADD dword ptr[A]; // Add A ( on Stack ) to it
    FMUL dword ptr[B]; //Multiply by B ( on Stack )
    FSTP dword ptr[A]; // Pop into A
    loop @StartOfLoop; //Dec loop counter
end;
May be
    sub ecx, 1; //Dec loop counter
    jnz @StartOfLoop;
instead of
    loop  @StartOfLoop;
can be better.

What are your results?

Variable alignment should have also a big influence. for details see:
IA-32 Intel® Architecture Optimization Reference Manual ftp://download.intel.com/design/Pentium4/manuals/24896611.pdf

Jan Ruzicka

Jan Ruzicka
to Raw Magick DOT COM: My name is Peter Dove, I am new to FPC and Lazarus. I come from a mainly Delphi…

Sorry for previous reply.
I overlooked use of the index variable X.

Which C compiler are you comparing the FPC with?
GCC, MSVC, CC (include version please)
Some compilers have a function optimization, some have just a statement optimization.

Could you post a whole example?
The whole program would prevent misunderstanding.
As your program could be rewritten using B as constant or the inner loop as :

  A := (A + X) * B;

How are you measuring the time?

Jan Ruzicka