Sign in Join

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

Mailing lists / fpc-devel / 2006-10

using sse2 packed doubles

20 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

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

Vincent Snijders

Hi,

I wondered if it is possible to add support for using sse2 packed doubles in fpc.

I tried to create a sse2doubles type and define a operator + on it. Eventually I want to move this type and all operators on it to a separate sse2 unit, so that the user of this type doesn't need to see the assembler. The following program works, but not as efficiently as I had hoped, because the operator is not inlined, probably because of the assembler code.

Is it possible to create this purely in RTL /units or is compiler support needed? Does it make sense to create a 'feature request'bug tracker item for this? Compiler hacking is out of my league.

program optsse2;
{$mode objfpc}{$H+}{$ASMMODE ATT}
{$FPUTYPE SSE2}
type
   sse2doubles = record
                  d1, d2: double;
                end;

operator + (d1, d2: sse2doubles) : sse2doubles; assembler; inline; {begin

   result.d1 := d1.d1 + d2.d1;
   result.d2 := d1.d2 + d2.d2;

end;}
asm
movupd (%eax), %xmm0
addpd (%edx), %xmm0
movupd %xmm0, (%ecx)
end;

procedure test;
var
   sd1, sd2, sd3: sse2doubles;
begin
   sd1.d1 := 1;
   sd1.d2 := 2;
   sd2.d1 := 3;
   sd2.d2 := 4;
   sd3 := sd1 + sd2;
   writeln(sd3.d1);
   writeln(sd3.d2);
end;
begin
   test;
end.

Reply by email

Florian Klaempfl
to Vincent Snijders
Show quoted text 11 lines
Hi, I wondered if it is possible to add support for using sse2 packed doubles in fpc. I tried to create a sse2doubles type and define a operator + on it. Eventually I want to move this type and all operators on it to a separate sse2 unit, so that the user of this type doesn't need to see the assembler. The following program works, but not as efficiently as I had hoped, because the operator is not inlined, probably because of the assembler code.

Indeed.

Is it possible to create this purely in RTL /units

No.

or is compiler support needed?

Yes.

Does it make sense to create a 'feature request'bug tracker item for this? Compiler hacking is out of my league.

Do you think it's allowed to use in the benchmarks?

Show quoted text 42 lines
program optsse2; {$mode objfpc}{$H+}{$ASMMODE ATT} {$FPUTYPE SSE2} type sse2doubles = record d1, d2: double; end; operator + (d1, d2: sse2doubles) : sse2doubles; assembler; inline; {begin result.d1 := d1.d1 + d2.d1; result.d2 := d1.d2 + d2.d2; end;} asm movupd (%eax), %xmm0 addpd (%edx), %xmm0 movupd %xmm0, (%ecx) end; procedure test; var sd1, sd2, sd3: sse2doubles; begin sd1.d1 := 1; sd1.d2 := 2; sd2.d1 := 3; sd2.d2 := 4; sd3 := sd1 + sd2; writeln(sd3.d1); writeln(sd3.d2); end; begin test; end. _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Vincent Snijders
to Florian Klaempfl: Indeed. No. Yes. Do you think it's allowed to use in the benchmarks?
Florian Klaempfl wrote 34 lines
Vincent Snijders schrieb: > Hi, > > I wondered if it is possible to add support for using sse2 packed > doubles in fpc. > > I tried to create a sse2doubles type and define a operator + on it. > Eventually I want to move this type and all operators on it to a > separate sse2 unit, so that the user of this type doesn't need to see > the assembler. The following program works, but not as efficiently as > I had hoped, because the operator is not inlined, probably because of > the assembler code. Indeed. > > Is it possible to create this purely in RTL /units No. > or is compiler support needed? Yes. > Does it make sense to create a 'feature request'bug tracker item for > this? Compiler hacking is out of my league. Do you think it's allowed to use in the benchmarks?

Yes, as long as it is part of the RTL. Look at
http://shootout.alioth.debian.org/gp4/benchmark.php?test=mandelbrot&lang=gcc&id=3

Using asm block would probably not allowed of course, but making use of sse2 support offered by the compiler and rtl is allowed.

So if fpc hypothetically would support a type double2 = array[0..1] of double and operators like + - * on it would use the packed double instructions of sse2, then it could be used in the shoutout benchmarks.

After all the following program looks perfectly normal pascal.

var
   a, b, c, d: double2;
begin
   d := a + b *c;
end.

Vincent

Reply by email

Micha Nelissen
to Vincent Snijders
Vincent Snijders wrote
So if fpc hypothetically would support a type double2 = array[0..1] of double and operators like + - * on it would use the packed double instructions of sse2, then it could be used in the shoutout benchmarks.

Isn't it better to generalize this support to vectors ? Maybe in the future it will support 3 or 4

Reply by email

Daniël Mantione
to Micha Nelissen

Op Fri, 6 Oct 2006, schreef Micha Nelissen:

Show quoted text 7 lines
Vincent Snijders wrote: > So if fpc hypothetically would support a type double2 = array[0..1] of > double and operators like + - * on it would use the packed double > instructions of sse2, then it could be used in the shoutout benchmarks. Isn't it better to generalize this support to vectors ? Maybe in the future it will support 3 or 4 a look at Vector Pascal, it contains language extensions to deal with

exactly this. (And FPC's MMX support could do this primitively as well.)

If we add language extenstions, I would like them to language wise compatible with the Vector Pascal extensions.

You could also start an assembler implementation of the matrix unit. I suppose using it is allowed, and a Tvector2_double looks a lot like such a double2.

Daniël

Reply by email

Vincent Snijders
to Micha Nelissen: It's also "just" pascal, you know ;-). Micha
Micha Nelissen wrote 7 lines
Vincent Snijders wrote: >tracker item for this? Compiler hacking is out of my league. It's also "just" pascal, you know ;-).

Sure, but modifying the compiler to reach my goal is an extra indirection, which makes it more difficult for me.

There is a difference between writing pascal programs and writing programs to compile pascal programs (I write). More is possible, but more possibilities doesn't mean easier.

Vincent

Reply by email

Vlad
to Vincent Snijders: Yes, as long as it is part of the RTL. Look at…

Hi.

I'm porting my Delphi app to Mac OS X. Fortunately my core classes compile fine with freepascal, so I can produce dylib and use it from Cocoa application. To make binaries I use fpc 2.0.4 for powerpc version and fpc 2.1.1 for intel version (2.1.1 snapshot downloaded on August 2, 2006 from lazarus section). Recently I started to test performance and found that rtl spends quite a lot of time on array finalization. If you look into rtti.inc file you may see that it walks on array and trying to finalize each item even though it may need no with dynamic arrays. And with dynamic arrays similar problem may happen if rtl needs to add reference to each item in array but of cource it could be that items are not reference counted, so it will walk on array and will call appropriate function which just have nothing to do. To solve this issue I added function into "rtti.inc" which determines is data type dynamic (and therefore needs finalization and could be refrence counted) or not. It just recursively checks data types and returns TRUE if data type needs finalization. Then in ArrayRTTI and fpc_finalize_array I call this function to check is it neccessary to finalize each item or not. I added this function to "aliases.inc" and use it from "dynarr.inc" too - there two places where int_addref() function called for each array item, so, before doing this now for me it checks is that neccessary or not.

Perhaps the best way is to include some kind of attribute into type info and determine at compile time is data type has any dynamic part or not. Perhaps compiler may even not generate finalization calls for records (I don't know - maybe this is already done in compiler). But this could be much bigger amount of work

I can provide more info if you need. And I think that even such change is already better than nothing...

Best regards.
Vlad.

Here is the code of my procedure:
--------------------------------------------------------------------
function fpc_is_dyn_type( TypeInfo:Pointer ): boolean;[Public,Alias : 'FPC_IS_DYN_TYPE'];  compilerproc;
var
  Temp : pbyte;
  namelen : byte;
  count,
  size,
  i : SizeInt;
  info : pointer;
begin
  case PByte(TypeInfo)^ of
  tkAstring, tkWstring, tkInterface, tkVariant, tkDynArray:
  Result := true;
  tkArray :
  begin
      Temp:=PByte(TypeInfo);
      inc(Temp);
      { Skip Name }
      namelen:=Temp^;
      inc(temp,namelen+1);
      temp:=aligntoptr(temp);
      { Skip Element size and count }
      inc(Temp,2*sizeof(sizeint));
      Result := fpc_is_dyn_type(PPointer(Temp)^);
    end;
  tkObject,
  tkRecord:
    begin
    Temp:=PByte(TypeInfo);
      inc(Temp);
      { Skip Name }
      namelen:=Temp^;
      inc(temp,namelen+1);
      temp:=aligntoptr(temp);
      { Skip size }
      inc(Temp,4);
      { Element count }
      count:=PLongint(Temp)^;
      inc(Temp,sizeof(longint));
      { Process elements }
      for i:=1 to count Do
        begin
          Info:=PPointer(Temp)^;
          { Skip offset }
          inc(Temp,sizeof(Info)+sizeof(longint));
          if fpc_is_dyn_type(Info) then
          begin
            Result := true;
            exit;
          end;
        end;
      Result := false;
  end;
  else
    Result := false;
  end;
end;
 ----------------------------------------------------------------------

Reply by email

Michael Van Canneyt
to Vlad
Vlad wrote
Hi. I'm porting my Delphi app to Mac OS X. Fortunately my core classes compile fine with freepascal, so I can produce dylib and use it from Cocoa application. To make binaries I use fpc 2.0.4 for powerpc version and fpc 2.1.1 for intel version (2.1.1 snapshot downloaded on August 2, 2006 from lazarus section). Recently I started to test performance and found that rtl spends quite a lot of time on array finalization. If you look into rtti.inc file you may see that it walks on array and trying to finalize each item even though it may need no with dynamic arrays. And with dynamic arrays similar problem may happen if rtl needs to add reference to each item in array but of cource it could be that items are not reference counted, so it will walk on array and will call appropriate function which just have nothing to do. To solve this issue I added function into "rtti.inc" which determines is data type dynamic (and therefore needs finalization and could be refrence counted) or not. It just recursively checks data types and returns TRUE if data type needs finalization. Then in ArrayRTTI and fpc_finalize_array I call this function to check is it neccessary to finalize each item or not. I added this function to "aliases.inc" and use it from "dynarr.inc" too - there two places where int_addref() function called for each array item, so, before doing this now for me it checks is that neccessary or not.

Did you see a change in performance ? Can you give an order of magnitude ?

Perhaps the best way is to include some kind of attribute into type info and determine at compile time is data type has any dynamic part or not. Perhaps compiler may even not generate finalization calls for records (I don't know - maybe this is already done in compiler). But this could be much bigger amount of work I can provide more info if you need. And I think that even such change is already better than nothing...

I think it may be indeed a better idea to see whether the compiler can set a flag in the type info:

If we change TTypeInfo to

   TTypeInfo = record
     Kind : TTypeKind;
     Finalize : Boolean;
     Name : ShortString;
     // here the type data follows as TTypeData record
   end;

Then there is no need for a recursive loop (the compiler has already done that...); Only the Finalize field needs to be retrieved. Putting this field in the middle will make sure 99,9% of all existing code will still work.

Peter, Florian, any comments ?

Michael.

Reply by email

Vincent Snijders
to Daniël Mantione: Op Fri, 6 Oct 2006, schreef Micha Nelissen: exactly this. (And FPC's MMX support could do…
Daniël Mantione wrote 9 lines
Op Fri, 6 Oct 2006, schreef Micha Nelissen: >Vincent Snijders wrote: > You could also start an assembler implementation of the matrix unit. I suppose using it is allowed, and a Tvector2_double looks a lot like such a double2.

Unless the compiler somehow helps, inlining the assembler implementation won't work and then the speedup might be lost again.

Vincent

Reply by email

Florian Klaempfl
to Vincent Snijders
Show quoted text 13 lines
Daniël Mantione wrote: > > Op Fri, 6 Oct 2006, schreef Micha Nelissen: > > >> Vincent Snijders wrote: >> > You could also start an assembler implementation of the matrix unit. I > suppose using it is allowed, and a Tvector2_double looks a lot like > such a double2. Unless the compiler somehow helps, inlining the assembler implementation won't work and then the speedup might be lost again.

I started to add vector pascal like support, currently only i386/x86_64 are supported (no generic support). The whole (currently implemented) functionality is demonstrated by the following example. Please give some feedback if it allows benchmark speedups.

var
   ad1,ad2,ad3 : array[0..1] of double;
   as1,as2,as3 : array[0..3] of single;
begin
   ad1:=ad2/ad3;
   ad1:=ad2+ad3;
   ad1:=ad2*ad3;
   ad1:=ad2-ad3;
   as1:=as2/as3;
   as1:=as2+as3;
   as1:=as2*as3;
   as1:=as2-as3;
end.

You need to use the experimental switch -Sv.

Reply by email

Daniël Mantione
to Florian Klaempfl

Op Sat, 7 Oct 2006, schreef Florian Klaempfl:

Show quoted text 19 lines
Vincent Snijders schrieb: > Daniël Mantione wrote: > > > > Op Fri, 6 Oct 2006, schreef Micha Nelissen: > > > > > > > Vincent Snijders wrote: > > > > > You could also start an assembler implementation of the matrix unit. > > I suppose using it is allowed, and a Tvector2_double looks a lot like > > such a double2. > > Unless the compiler somehow helps, inlining the assembler implementation > won't work and then the speedup might be lost again. I started to add vector pascal like support, currently only i386/x86_64 are supported (no generic support). The whole (currently implemented) functionality is demonstrated by the following example. Please give some feedback if it allows benchmark speedups.

To get a large speedup, I think you should instead of making pairs of doubles, do the pixels in parallel. I.e. in this benchmark, a row is 3000 pixels wide, so, make an array of 3000 doubles, and do the operation with arrays. With proper compiler optimization, it should be possible to achieve speeds close to 2 flops a clock cycle.

Daniël

Reply by email

Florian Klaempfl
to Daniël Mantione
Show quoted text 25 lines
Op Sat, 7 Oct 2006, schreef Florian Klaempfl: > Vincent Snijders schrieb: >> Daniël Mantione wrote: >>> Op Fri, 6 Oct 2006, schreef Micha Nelissen: >>> >>> >>>> Vincent Snijders wrote: >>>> >>> You could also start an assembler implementation of the matrix unit. >>> I suppose using it is allowed, and a Tvector2_double looks a lot like >>> such a double2. >> Unless the compiler somehow helps, inlining the assembler implementation >> won't work and then the speedup might be lost again. > I started to add vector pascal like support, currently only i386/x86_64 are > supported (no generic support). The whole (currently implemented) > functionality is demonstrated by the following example. Please give some > feedback if it allows benchmark speedups. To get a large speedup, I think you should instead of making pairs of doubles, do the pixels in parallel. I.e. in this benchmark, a row is 3000 pixels wide, so, make an array of 3000 doubles, and do the operation with arrays. With proper compiler optimization, it should be possible to achieve speeds close to 2 flops a clock cycle.

This is planned to be done, but currently it only spites an ie out :)

Reply by email

Vincent Snijders
to Florian Klaempfl: I started to add vector pascal like support, currently only i386/x86_64 are supported (no…
Florian Klaempfl wrote 23 lines
Vincent Snijders schrieb: > Daniël Mantione wrote: > >> >> Op Fri, 6 Oct 2006, schreef Micha Nelissen: >> >> >>> Vincent Snijders wrote: >>> >> You could also start an assembler implementation of the matrix unit. >> I suppose using it is allowed, and a Tvector2_double looks a lot like >> such a double2. > > > Unless the compiler somehow helps, inlining the assembler > implementation won't work and then the speedup might be lost again. I started to add vector pascal like support, currently only i386/x86_64 are supported (no generic support). The whole (currently implemented) functionality is demonstrated by the following example. Please give some feedback if it allows benchmark speedups.

Thanks Florian, for starting the vector support.

I think this would help speedup in benchmarks. I cannot give real estimates how much, maybe 20 % or so.

There are some problems still some bugs (or things not implemented).

Given the following program:

var
   ad1,ad2,ad3 : array[0..1] of double;
begin
   ad2[0] := 1;
   ad2[1] := 3;
   ad3[0] := 9;
   ad2[1] := 12;
   ad1:=ad2+ad3;
   writeln(ad1[1]);
end.

It writes:
0.000000000000000E+000

Looking at the assembler, I see the ad1 in the writeln is read from memory, but the ad1 is still only in the xmm0% register.

Further I encountered problems with the alignment.

Vincent

Vincent

Reply by email

Vincent Snijders
to Daniël Mantione: Op Sat, 7 Oct 2006, schreef Florian Klaempfl: To get a large speedup, I think you should…
Daniël Mantione wrote 18 lines
Op Sat, 7 Oct 2006, schreef Florian Klaempfl: >Vincent Snijders schrieb: > >I started to add vector pascal like support, currently only i386/x86_64 are >supported (no generic support). The whole (currently implemented) >functionality is demonstrated by the following example. Please give some >feedback if it allows benchmark speedups. To get a large speedup, I think you should instead of making pairs of doubles, do the pixels in parallel. I.e. in this benchmark, a row is 3000 pixels wide, so, make an array of 3000 doubles, and do the operation with arrays. With proper compiler optimization, it should be possible to achieve speeds close to 2 flops a clock cycle.

The 'problem' in this benchmark is that the number of iterations of the inner loop isn't fixed, but can vary between 1 and 50. If you pair two doubles, the change you can break the loop for all elements of the vector before iteration 50 is bigger than when you combine 3000 elements.

Vincent

Reply by email

Marco van de Voort
to Vincent Snijders
The 'problem' in this benchmark is that the number of iterations of the inner loop isn't fixed, but can vary between 1 and 50. If you pair two doubles, the change you can break the loop for all elements of the vector before iteration 50 is bigger than when you combine 3000 elements.

There was an article about such loops in one of the recent German C'T 's, where they applied it to their standard benchmark, a mandelbrot fractal calculator afaik.

Reply by email

Daniël Mantione
to Vincent Snijders: The 'problem' in this benchmark is that the number of iterations of the inner loop isn't…

Op Sun, 8 Oct 2006, schreef Vincent Snijders:

Show quoted text 26 lines
Daniël Mantione wrote: > > Op Sat, 7 Oct 2006, schreef Florian Klaempfl: > > > > Vincent Snijders schrieb: > > > > I started to add vector pascal like support, currently only > > i386/x86_64 are > > supported (no generic support). The whole (currently implemented) > > functionality is demonstrated by the following example. Please give > > some > > feedback if it allows benchmark speedups. > > > To get a large speedup, I think you should instead of making pairs of > doubles, do the pixels in parallel. I.e. in this benchmark, a row is 3000 > pixels wide, so, make an array of 3000 doubles, and do the operation with > arrays. With proper compiler optimization, it should be possible to > achieve speeds close to 2 flops a clock cycle. > The 'problem' in this benchmark is that the number of iterations of the inner loop isn't fixed, but can vary between 1 and 50. If you pair two doubles, the change you can break the loop for all elements of the vector before iteration 50 is bigger than when you combine 3000 elements.

You are right. How about doing it in blocks of 8x8 pixels? The high iteration loops are concentrated close to the borders of the set, so for most blocks the iteration can then be ended early.

Daniël

Reply by email

Vincent Snijders
to Daniël Mantione
Daniël Mantione wrote 36 lines
Op Sun, 8 Oct 2006, schreef Vincent Snijders: >Daniël Mantione wrote: > >>Op Sat, 7 Oct 2006, schreef Florian Klaempfl: >> >> >> >>>Vincent Snijders schrieb: >>> >>>I started to add vector pascal like support, currently only >>>i386/x86_64 are >>>supported (no generic support). The whole (currently implemented) >>>functionality is demonstrated by the following example. Please give >>>some >>>feedback if it allows benchmark speedups. >> >> >>To get a large speedup, I think you should instead of making pairs of >>doubles, do the pixels in parallel. I.e. in this benchmark, a row is 3000 >>pixels wide, so, make an array of 3000 doubles, and do the operation with >>arrays. With proper compiler optimization, it should be possible to >>achieve speeds close to 2 flops a clock cycle. >> > >The 'problem' in this benchmark is that the number of iterations of the inner >loop isn't fixed, but can vary between 1 and 50. If you pair two doubles, the >change you can break the loop for all elements of the vector before iteration >50 is bigger than when you combine 3000 elements. You are right. How about doing it in blocks of 8x8 pixels? The high iteration loops are concentrated close to the borders of the set, so for most blocks the iteration can then be ended early.

For starters I was thinking about blocks of 1x2 pixels ;-). The current hardware doesn't allow any more parallelism anyway. Or am I making a mistake in my thinking now?

Vincent

Reply by email

Daniël Mantione
to Vincent Snijders

Op Sun, 8 Oct 2006, schreef Vincent Snijders:

Show quoted text 7 lines
> You are right. How about doing it in blocks of 8x8 pixels? The high > iteration loops are concentrated close to the borders of > the set, so for most blocks the iteration can then be ended early. For starters I was thinking about blocks of 1x2 pixels ;-). The current hardware doesn't allow any more parallelism anyway. Or am I making a mistake in my thinking now?

Yes. Let's say a pixel is calculated by a*b*c*d. If you calculate the expression serially each instruction depends on the result of the previous one. This is bad for pipelining; the floating point pipeline can only do 2 flops/cycle is the results do not depend on each other. If you first multiply a with b for all pixels, then with c for all pixels, then with d, the result of a multiplication does not depend on the previous one, and you get much higher throughput. At work I have to deal with this a lot to make applications perform fastest. My record with the HPL benchmark is 92,9% of the theoretical limit of an Opteron core.

Intel's latest cpu's can do 4 flops/cycle, which can only be realistically achieved when doing these kind of parallel processing.

Never mind, lets do what is easiest first :)

Daniël

Reply by email

Vincent Snijders
to Florian Klaempfl: This is planned to be done, but currently it only spites an ie out :)
Florian Klaempfl wrote 10 lines
Daniël Mantione schrieb: > > To get a large speedup, I think you should instead of making pairs of > doubles, do the pixels in parallel. I.e. in this benchmark, a row is > 3000 pixels wide, so, make an array of 3000 doubles, and do the > operation with arrays. With proper compiler optimization, it should be > possible to achieve speeds close to 2 flops a clock cycle. This is planned to be done, but currently it only spites an ie out :)

I added the spectralnorm benchmark to svn:
http://svn.freepascal.org/svn/fpc/trunk/tests/bench/shootout/src/spectralnorm.pp

It can serve as an example where large vectors can be used. The shootout uses n=2500.

Vincent

Reply by email