🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.delphi.basm
26 messages
26 total messages Started by Dennis Wed, 18 Sep 2002 07:51
Move Optimization
#18
Author: Dennis
Date: Wed, 18 Sep 2002 07:51
15 lines
1405 bytes
Hi

Proposal for benchmark rules

Memory chunks of many different sizes must be part of the benchmark.
Ranging from a few bytes to some M bytes.
The benchmark must run on cached memory as well as uncached.
Memory chunks must be aligned as well as unaligned (but no worse than
what we get from the memory manager).

Sub benchmarks are run under the described conditions and their values
are summed up to give the final benchmark. Sub benchmarks must attribute
evenly in the final benchmark.

Dennis
Re: Move Optimization
#23
Author: Eric Grange
Date: Wed, 18 Sep 2002 08:33
11 lines
1516 bytes
> (but no worse than what we get from the memory manager).

I wouldn't use that restriction, Move can be used in string
functions, with char alignment, and thus a higher probability
of having at least one unaligned block of memory rather than an 
aligned block (f.i. in Concat, Copy, Insert and Delete...).
(when transfering large blocks, the cost of handling the unaligned
bytes separately is negligible, and for small blocks, it may help
you save the setup costs of a 'rep movsd' or MMX transfer).

Eric
Re: Move Optimization
#24
Author: Dennis
Date: Wed, 18 Sep 2002 10:55
20 lines
1654 bytes
Hi Eric

Then we should measure at blocks with these alignments: none, 4byte, 8
byte, 16byte, 32byte?

Dennis

Eric Grange wrote:

> > (but no worse than what we get from the memory manager).
>
> I wouldn't use that restriction, Move can be used in string
> functions, with char alignment, and thus a higher probability
> of having at least one unaligned block of memory rather than an
> aligned block (f.i. in Concat, Copy, Insert and Delete...).
> (when transfering large blocks, the cost of handling the unaligned
> bytes separately is negligible, and for small blocks, it may help
> you save the setup costs of a 'rep movsd' or MMX transfer).
>
> Eric
Re: Move Optimization
#31
Author: Hagen Reddmann
Date: Wed, 18 Sep 2002 16:55
15 lines
1460 bytes
Better 1,2,3,4 thats the most probable cases. Better we don't use any
restrictions because Delphis move() have no restrictions, and any
replacement schoud'nt have it.

Hagen

"Dennis" <marianndennis@email.dk> schrieb im Newsbeitrag
news:3D885BB8.33FB9B50@email.dk...
> Hi Eric
>
> Then we should measure at blocks with these alignments: none, 4byte, 8
> byte, 16byte, 32byte?
>
> Dennis
>
Re: Move Optimization
#39
Author: Robert Lee
Date: Wed, 18 Sep 2002 22:11
13 lines
1543 bytes
Hagen Reddmann wrote:

> Better 1,2,3,4 thats the most probable cases. Better we don't use any
> restrictions because Delphis move() have no restrictions, and any
> replacement schoud'nt have it.

I agree, it has to cover all possible cases (including the dreaded overlap
case).  The key for big moves is to split off the unaligned ends to do
separately.  Plus you really want to streamline the path for small moves.

--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#40
Author: John O'Harrow
Date: Wed, 18 Sep 2002 23:14
25 lines
1986 bytes
A useful starting point for an optimised move procedure might be my Pentium
and Athlon move procedures as listed in the thread "MMX functions".  These
optimised procedures work with any data size and use prefetching and MMX,
but do not yet allow overlapping blocks.

John O'Harrow


"Robert Lee" <rhlee@optimalcode.com> wrote in message
news:3D88FA1D.153D60A1@optimalcode.com...
> Hagen Reddmann wrote:
>
> > Better 1,2,3,4 thats the most probable cases. Better we don't use any
> > restrictions because Delphis move() have no restrictions, and any
> > replacement schoud'nt have it.
>
> I agree, it has to cover all possible cases (including the dreaded overlap
> case).  The key for big moves is to split off the unaligned ends to do
> separately.  Plus you really want to streamline the path for small moves.
>
> --
> Bob Lee
> High Performance Delphi - http://www.optimalcode.com/
>
>
Re: Move Optimization
#43
Author: Dennis
Date: Thu, 19 Sep 2002 07:38
32 lines
2097 bytes
Hi John

Yes. Throw them in here as your suggestions.

Dennis

John O'Harrow wrote:

> A useful starting point for an optimised move procedure might be my Pentium
> and Athlon move procedures as listed in the thread "MMX functions".  These
> optimised procedures work with any data size and use prefetching and MMX,
> but do not yet allow overlapping blocks.
>
> John O'Harrow
>
> "Robert Lee" <rhlee@optimalcode.com> wrote in message
> news:3D88FA1D.153D60A1@optimalcode.com...
> > Hagen Reddmann wrote:
> >
> > > Better 1,2,3,4 thats the most probable cases. Better we don't use any
> > > restrictions because Delphis move() have no restrictions, and any
> > > replacement schoud'nt have it.
> >
> > I agree, it has to cover all possible cases (including the dreaded overlap
> > case).  The key for big moves is to split off the unaligned ends to do
> > separately.  Plus you really want to streamline the path for small moves.
> >
> > --
> > Bob Lee
> > High Performance Delphi - http://www.optimalcode.com/
> >
> >
Re: Move Optimization
#45
Author: Dennis
Date: Thu, 19 Sep 2002 07:42
28 lines
1871 bytes
Hi

We can make it a rule that the one proposing a function writes the benchmark
code. Respecting all votes and proposals of course.

I write the code for the move benchmark, but i would like you all to paste
some code that exemplifies your proposal for rules. This will help me
getting started and make the benchmark more varied and realistic.

Dennis

Dennis wrote:

> Hi
>
> Proposal for benchmark rules
>
> Memory chunks of many different sizes must be part of the benchmark.
> Ranging from a few bytes to some M bytes.
> The benchmark must run on cached memory as well as uncached.
> Memory chunks must be aligned as well as unaligned (but no worse than
> what we get from the memory manager).
>
> Sub benchmarks are run under the described conditions and their values
> are summed up to give the final benchmark. Sub benchmarks must attribute
> evenly in the final benchmark.
>
> Dennis
Re: Move Optimization
#47
Author: Dennis
Date: Thu, 19 Sep 2002 07:55
22 lines
1733 bytes
Hi

We also need to agree on some test that the optimized function need to pass
to be part of the competition/project.

In case of move it is easy to write some code that initializes two arrays
with different data, copies one to the other and verify they are identical.
How do we test that it properly handles overlapped destination and source?
Should we make a test for; static arrays, dynamic arrays, strings, PChars
etc?

Is it OK that the move function reads out of the source start/end? If the
standard move does not I vote no.
Writing out of bounds is not allowed?

Should we explicitly test that ebx, edi & esi registers are preserved?

Any other proposals for test cases?

It will make me happy if you supply code for proposed testcases :-)

Dennis
Re: Move Optimization
#49
Author: Eric Grange
Date: Thu, 19 Sep 2002 08:05
28 lines
2349 bytes
> Should we make a test for; static arrays, dynamic arrays, strings, PChars
> etc?

PChars or binary memory should be enough (you can cover all the cases
easily with them, it's just a matter of allocating a big buffer and positionning
pointers inside it).

> Is it OK that the move function reads out of the source start/end? If the
> standard move does not I vote no.
> Writing out of bounds is not allowed?

I say no for both, because the bytes you may read/write may stand in protected
memory of some kind (there is a recent thread in the NG on a similar problem
affecting Boolean assignment, the one with mov vs mozx vs movsx).

> Should we explicitly test that ebx, edi & esi registers are preserved?

I say yes, though not preserving them will probably crash the test proggy
on short notice anyway. There should also be some FPU code to make sure
no EMMS/FEMMS has been forgotten if MMX is used (the FPU code will fail
if EMMS has been forgotten).

There is also the problem of cache-related instruction (movntq, prefetch...)
which can give a boost to Move in some benchmark situations, but kill 
performance in real-world cases (movntq and similar instructions can be
particularly dangerous from that point of view in my experience).

Eric
Re: Move Optimization
#110
Author: Dennis
Date: Thu, 19 Sep 2002 18:15
117 lines
5247 bytes
Hi

This is the first proposal for benchmark code. All suggestions for
modifications are welcome.
Missing is control over data alignment and control over caching.

These array sizes are "moved";
//8;11;15;20;27;36;49;66;89;120;162;219;296;400;540;729;984;1328;1793;2421;3268;4412;5956;8041;10855;14654;19783;26707;36054;48673;65709;88707;119754;161668;218252;294640;397764;536981;724924;978647;1321173;1783584;

The average copy speed in MB/s is the benchmark.
There is no cache flush between runs.
There is no code for data alignment.

procedure TMainForm.RunButtonClick(Sender: TObject);
var
 SourceArray : array[0..1999999] of Byte;
 DestinationArray : array[0..1999999] of Byte;
 I, K, Kmax, RunNo : Cardinal;
 NoOfBytesPerSec, NoOfMBytesPerSec, NoOfMBytesPerSecTotal,
NoOfMBytesPerSecMax,
 NoOfMBytesPerSecMin : Int64;
 NoOfBytesFP, RunTimeSec : Extended;
 ArraySize, NoOfBytes : Int64;
 X, Y : Pointer;
 lpFrequency, lpPerformanceCount, StartCount, EndCount,
 NoOfTicks : TLargeInteger;
 Succes : Boolean;
const
 ARRAYGROWFACTOR : Double = 1.35;
 ARRAYSIZESTART : Cardinal = 8;
 RUNNOMAX : Cardinal = 20000000; //Must be bigger than Arraysize or some
bick blocks will not be run

begin
 StatusBar1.SimpleText := 'Running';
 SummedNoOfMBytesPerSecEdit.Text := '';
 SummedArraySizeEdit.Text := '';
 NoOfMBytesPerSecMaxEdit.Text := '';
 X := @SourceArray;
 Y := @DestinationArray;
 AddressSourceEdit.Text := IntToHex(Integer(X), 8);
 AddressDestEdit.Text := IntToHex(Integer(Y), 8);
 ProgressBar1.Max := 42;
 ArraySize := ARRAYSIZESTART;
 NoOfMBytesPerSecTotal := 0;
 NoOfMBytesPerSecMax := 0;
 NoOfMBytesPerSecMin := 99999999;
 //Initialize arrays
 for I := 0 to 1999999 do
  begin
   SourceArray[I] := 255;
   DestinationArray[I] := 1;
  end;
 Succes := QueryPerformanceFrequency(lpFrequency);
 if not Succes then
  raise Exception.Create('QueryPerformanceFrequency failed');
 RunNo := 1;
 while (ArraySize < 1999999) do
  begin
   SummedArraySizeEdit.Text := SummedArraySizeEdit.Text +
IntToStr(ArraySize) + ';';
   if ArraySize > Length(DestinationArray) then
    raise Exception.Create('ArraySize to big');
   ArraySizeLabeledEdit.Text := IntToStr(ArraySize);
   Update;
   Kmax := Round(RUNNOMAX / ArraySize);//Adjust Kmax because more runs are
needed on small blocks than on big blocks
   Succes := QueryPerformanceCounter(lpPerformanceCount);
   if Succes then
    StartCount := lpPerformanceCount
   else
    raise Exception.Create('QueryPerformanceCounter failed');
   for K := 1 to Kmax do
    begin
     //Functions to benchmark
     Move(SourceArray, DestinationArray, ArraySize);
    end;
   Succes := QueryPerformanceCounter(lpPerformanceCount);
   if Succes then
    EndCount := lpPerformanceCount
   else
    raise Exception.Create('QueryPerformanceCounter failed');
   NoOfTicks := EndCount - StartCount;
   RunTimeSec := NoOfTicks / lpFrequency;
   RunTimeEdit.Text := FloatToStrF(RunTimeSec, ffFixed, 9, 1);
   NoOfBytes := ArraySize * Kmax;
   NoOfBytesFP := NoOfBytes;
   if RunTimeSec <> 0 then
    NoOfBytesPerSec := Round(NoOfBytesFP / RunTimeSec)
   else
    NoOfBytesPerSec := 0;
   NoOfMBytesPerSec := Round(NoOfBytesPerSec / 1024 / 1024);
   NoOfMBytesPerSecEdit.Text := IntToStr(NoOfMBytesPerSec);
   NoOfMBytesPerSecTotal := NoOfMBytesPerSecTotal + NoOfMBytesPerSec;
   NoOfMBytesPerSecTotalEdit.Text := IntToStr(NoOfMBytesPerSecTotal);
   NoOfMBytesPerSecAverageEdit.Text := FloatToStrF(NoOfMBytesPerSecTotal /
RunNo, ffFixed, 9, 0);
   if NoOfMBytesPerSec < NoOfMBytesPerSecMin then
    NoOfMBytesPerSecMin := NoOfMBytesPerSec;
   NoOfMBytesPerSecMinEdit.Text := IntToStr(NoOfMBytesPerSecMin);
   if NoOfMBytesPerSec > NoOfMBytesPerSecMax then
    NoOfMBytesPerSecMax := NoOfMBytesPerSec;
   NoOfMBytesPerSecMaxEdit.Text := IntToStr(NoOfMBytesPerSecMax);
   SummedNoOfMBytesPerSecEdit.Text :=  SummedNoOfMBytesPerSecEdit.Text +
NoOfMBytesPerSecEdit.Text + ';';
   ProgressBar1.Position := RunNo;
   ArraySize := Round(ArraySize * ARRAYGROWFACTOR);
   Inc(RunNo);
   Update;
  end;
 RichEdit1.Lines.Add(SummedNoOfMBytesPerSecEdit.Text);
 RichEdit1.Lines.SaveToFile('C:\Temp\MeMoveTiming.Txt');//Ready for import
into Excel
 StatusBar1.SimpleText := 'Finished';
end;

Dennis
Re: Move Optimization
#109
Author: Dennis
Date: Thu, 19 Sep 2002 18:20
131 lines
5818 bytes
Hi

This is the second proposal for benchmark code.
It is an extension of the first benchmark. Here move is called twice in
every loop. Once with the same array size as before. Once with an arraysize
which is MaxSize = 2MB - Arraysize. Thus every loop copies 2 MB and the two
moves competes on the cache and lots of cache misses is the result.

//This benchmark uses four static arrays of byte, size 2MB
//Move is called repeatedly on two sets of arrays. One array is of size X
and the other is MaxSize - X
//The two array sets competes on the cache, which is never big enough to
hold all 4 arrays
//The number of bytes "moved" is always MaxSize
//As X changes the ration between array sizes change
//The move speed is recorded for each X
//After N runs statistics is computed; Max -, Min - & Average - copy speed
//Average copy speed is the benchmark

procedure TMainForm.RunButtonClick(Sender: TObject);
var
 SourceArray1 : array[1..2000000] of Byte;
 DestinationArray1 : array[1..2000000] of Byte;
 SourceArray2 : array[1..2000000] of Byte;
 DestinationArray2 : array[1..2000000] of Byte;
 I, K, Kmax, RunNo : Cardinal;
 NoOfBytesPerSec, NoOfMBytesPerSec, NoOfMBytesPerSecTotal,
NoOfMBytesPerSecMax,
 NoOfMBytesPerSecMin : Int64;
 NoOfBytesFP, RunTimeSec : Extended;
 ArraySize, NoOfBytes : Int64;
 X1, X2, Y1, Y2 : Pointer;
 lpFrequency, lpPerformanceCount, StartCount, EndCount,
 NoOfTicks : TLargeInteger;
 Succes : Boolean;
const
 ARRAYGROWFACTOR : Double = 1.35;
 ARRAYSIZESTART : Cardinal = 8;
 RUNNOMAX : Cardinal = 100;
 ARRAYSIZEMAX : Cardinal = 2000000;

begin
 StatusBar1.SimpleText := 'Running';
 SummedNoOfMBytesPerSecEdit.Text := '';
 SummedArraySizeEdit.Text := '';
 NoOfMBytesPerSecMaxEdit.Text := '';
 X1 := @SourceArray1;
 Y1 := @DestinationArray1;
 X2 := @SourceArray2;
 Y2 := @DestinationArray2;
 AddressSourceEdit.Text := IntToHex(Integer(X1), 8) + '/' +
IntToHex(Integer(X2), 8);
 AddressDestEdit.Text := IntToHex(Integer(Y1), 8) + '/' +
IntToHex(Integer(Y2), 8);
 ProgressBar1.Max := 42;
 ArraySize := ARRAYSIZESTART;
 NoOfMBytesPerSecTotal := 0;
 NoOfMBytesPerSecMax := 0;
 NoOfMBytesPerSecMin := 99999999;
 //Initialize arrays
 for I := 1 to ARRAYSIZEMAX do
  begin
   SourceArray1[I] := 255;
   DestinationArray1[I] := 1;
   SourceArray2[I] := 255;
   DestinationArray2[I] := 1;
  end;
 Succes := QueryPerformanceFrequency(lpFrequency);
 if not Succes then
  raise Exception.Create('QueryPerformanceFrequency failed');
 RunNo := 1;
 while (ArraySize < ARRAYSIZEMAX) do
  begin
   SummedArraySizeEdit.Text := SummedArraySizeEdit.Text +
IntToStr(ArraySize) + ';';
   if ArraySize > Length(DestinationArray1) then
    raise Exception.Create('ArraySize to big');
   ArraySizeLabeledEdit.Text := IntToStr(ArraySize);
   Update;
   Kmax := RUNNOMAX;
   Succes := QueryPerformanceCounter(lpPerformanceCount);
   if Succes then
    StartCount := lpPerformanceCount
   else
    raise Exception.Create('QueryPerformanceCounter failed');
   for K := 1 to Kmax do
    begin
     //Functions to benchmark
     Move(SourceArray1, DestinationArray1, ArraySize);
     Move(SourceArray2, DestinationArray2, ARRAYSIZEMAX - ArraySize);
    end;
   Succes := QueryPerformanceCounter(lpPerformanceCount);
   if Succes then
    EndCount := lpPerformanceCount
   else
    raise Exception.Create('QueryPerformanceCounter failed');
   NoOfTicks := EndCount - StartCount;
   RunTimeSec := NoOfTicks / lpFrequency;
   RunTimeEdit.Text := FloatToStrF(RunTimeSec, ffFixed, 9, 1);
   NoOfBytes := ARRAYSIZEMAX * Kmax;
   NoOfBytesFP := NoOfBytes;
   if RunTimeSec <> 0 then
    NoOfBytesPerSec := Round(NoOfBytesFP / RunTimeSec)
   else
    NoOfBytesPerSec := 0;
   NoOfMBytesPerSec := Round(NoOfBytesPerSec / 1024 / 1024);
   NoOfMBytesPerSecEdit.Text := IntToStr(NoOfMBytesPerSec);
   NoOfMBytesPerSecTotal := NoOfMBytesPerSecTotal + NoOfMBytesPerSec;
   NoOfMBytesPerSecTotalEdit.Text := IntToStr(NoOfMBytesPerSecTotal);
   NoOfMBytesPerSecAverageEdit.Text := FloatToStrF(NoOfMBytesPerSecTotal /
RunNo, ffFixed, 9, 0);
   if NoOfMBytesPerSec < NoOfMBytesPerSecMin then
    NoOfMBytesPerSecMin := NoOfMBytesPerSec;
   NoOfMBytesPerSecMinEdit.Text := IntToStr(NoOfMBytesPerSecMin);
   if NoOfMBytesPerSec > NoOfMBytesPerSecMax then
    NoOfMBytesPerSecMax := NoOfMBytesPerSec;
   NoOfMBytesPerSecMaxEdit.Text := IntToStr(NoOfMBytesPerSecMax);
   SummedNoOfMBytesPerSecEdit.Text :=  SummedNoOfMBytesPerSecEdit.Text +
NoOfMBytesPerSecEdit.Text + ';';
   ProgressBar1.Position := RunNo;
   ArraySize := Round(ArraySize * ARRAYGROWFACTOR);
   Inc(RunNo);
   Update;
  end;
 RichEdit1.Lines.Add(SummedNoOfMBytesPerSecEdit.Text);
 RichEdit1.Lines.SaveToFile('C:\Temp\MeMoveTiming.Txt');//Ready for import
into Excel
 StatusBar1.SimpleText := 'Finished';
end;

Dennis
Re: Move Optimization
#114
Author: Eric Grange
Date: Fri, 20 Sep 2002 08:17
15 lines
1745 bytes
> What do you specifically mean by "binary memory"?

Nothing special, just what you get from a GetMem f.i.
(ie. untyped bunch of bytes)

> Thats not a problem as i se it. They will just give bad benchmark results and be
> sorted out. It is crucial that our benchmarks are very realistic.

That may not be easy, the benchmark will do lots of moves, the movntq
performance loss happen if you move and then perform something on the moved
data (or part of the moved data, that would have been in cache otherwise,
but isn't due to the novntq). If you just move lots of bytes around without 
caring about them, movntq looks plenty fast.

Eric
Re: Move Optimization
#115
Author: Robert Lee
Date: Fri, 20 Sep 2002 11:29
12 lines
1357 bytes
Dennis wrote:

> Hi
>
> This is the first proposal for benchmark code. All suggestions for
> modifications are welcome.

You have no overlapped cases, and no tiny moves.   Don't forget that move is used throughout the RTL without consideration as to the size of move.  Consequently, tiny moves need to be very streamlined.

--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#116
Author: Dennis
Date: Fri, 20 Sep 2002 13:34
23 lines
1710 bytes
Hi

I thought these very tiny 8;11;15;20;27;36.

But ok i will set up a benchmark including more small ones and the overlapped case.
Does anybody have a good example of an overlapped move from real life?

Dennis

Robert Lee wrote:

> Dennis wrote:
>
> > Hi
> >
> > This is the first proposal for benchmark code. All suggestions for
> > modifications are welcome.
>
> You have no overlapped cases, and no tiny moves.   Don't forget that move is used throughout the RTL without consideration as to the size of move.  Consequently, tiny moves need to be very streamlined.
>
> --
> Bob Lee
> High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#117
Author: Robert Lee
Date: Fri, 20 Sep 2002 13:41
19 lines
1559 bytes
Dennis wrote:

> Hi
>
> I thought these very tiny 8;11;15;20;27;36.

How about 0-15, also 30 on a $XXXX XXX1 alignment is an interesting worst case.

BTW: see my other message.  If we split the move problem into two pieces it would significantly simplify the benchmarking.


> But ok i will set up a benchmark including more small ones and the overlapped case.
> Does anybody have a good example of an overlapped move from real life?

How about deleting an element from an array?

--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#118
Author: Robert Lee
Date: Fri, 20 Sep 2002 13:51
13 lines
1420 bytes
Hmm, might need an intermediate DWord aligned routine as well.

> procedure AlignedFwdMove()  // assumes 16byte alignment with count divisible
> by 16
> procedure FwdMove()  // calls AlignedFwdMove for the aligned center of the
> move
> procedure RevMove() // handles overlap cases
> procedure Move()  // handle all cases, calls FwdMove and RevMove as needed
>

--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#119
Author: Per Larsen
Date: Fri, 20 Sep 2002 15:23
21 lines
1665 bytes
Bob,

Warning: There's a potentially serious bug in your ReplaceRoutine
method.

>  Data.Distance:=Integer(NewAddr)-(Integer(Addr)+5);

That should be

 Data.Distance:=Integer(NewAddr)-Integer(Addr)-5;

Your version assumes the new address is higher than the original. IOW,
this will actually never become an issue if you're using code linked
into the app as a direct replacement (as you're doing here) since System
always comes first in memory, but in case you ever want to use this for
something else...

I just worked with some similar code yesterday, which is why this jumped
out at me.

- Per
Re: Move Optimization
#120
Author: Per Larsen
Date: Fri, 20 Sep 2002 15:26
11 lines
1385 bytes
Dennis,

IMO, the best thing to do would be to rig a real existing app with a
Move, which logged all calls to a data file with information about
source alignment, target alignment, size, and overlapped/not. That data
could then be used as input to the benchmark.

This would also supply real-life data about what cases are more
important to optimize for.

- Per
Re: Move Optimization
#121
Author: Dennis
Date: Fri, 20 Sep 2002 15:58
21 lines
1295 bytes
Hi

Can anybody tell me why this code is bad.
Move destroys the source string.
I believed S1 had to survive the Move.

var
 S1, S2 : AnsiString;

begin
 S1 := 'Dennis';
 S2 := 'XXXXXX';
 Edit1.Text := S1;
 Edit2.Text := S2;
 Move(S1, S2, 6);
 Edit3.Text := S1;
 Edit4.Text := S2;
end;

Thanks
Dennis
Re: Move Optimization
#123
Author: Robert Lee
Date: Fri, 20 Sep 2002 16:37
10 lines
1260 bytes
> @E:
>   mov cx, [eax+12]
>   mov [edx+12], cx

word sized access is "bad".  It causes stalls on some(all?) CPU's

I'll look int FwdMove tonite.
--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#122
Author: Robert Lee
Date: Fri, 20 Sep 2002 16:38
14 lines
1325 bytes

Per Larsen wrote:

> Bob,
>
> Warning: There's a potentially serious bug in your ReplaceRoutine
> method.

Subtle, but important.  Thanks,

--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#124
Author: Robert Lee
Date: Fri, 20 Sep 2002 16:53
24 lines
1687 bytes
Eric Grange wrote:

> Looks fair to me, slight issue: the pascal Move
> doesn't handle overlap for length 2-4.

Oops,  I didn't mean to leave that one in there.  It isn't ready for primetime yet.

> @F:
>   mov cl, [eax+14]  // source[count-15+0]
>   mov [edx+14], bl
> @E:
>   mov cx, [eax+12]
>   mov [edx+12], cx

Two problems:
    1) this is supposed to be a *forward* move.  You are copying backwards here.  I had a FreeMove version that worked like this but it is an unnecessary complication

    2) word-sized access is "bad" as it causes a CPU stall on some (all?) processors.

I'll look at it more tonite.

--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Re: Move Optimization
#125
Author: Hagen Reddmann
Date: Fri, 20 Sep 2002 17:12
46 lines
2611 bytes
How about to collect first some realtime inputs of Move() calls or GetMem()
and so on.
I think before we optimize such procedures we should collect the most
provable best/worstcase.
After that we can think about our design strategies.

Thats the main problem with such ideas as using MMX/SSE2 move()
replacements. All they gave on my trials bad performances because they are
only fast  in very rare circumstances. And second, they proposed performance
are far from reality and in fact says nothing about the real performance
with highly randomized realtime applications.

Why should be an high optimized MMX/SSE2 move() that requiere 1Mb moves,
faster when I do it in ten years 1000 times, and 1 trillion times i do moves
of length smaller than 64 bytes ?!


Best regards, Hagen


"Robert Lee" <rhlee@optimalcode.com> schrieb im Newsbeitrag
news:3D8B259B.C88CD284@optimalcode.com...
> Dennis wrote:
>
> > Hi
> >
> > I thought these very tiny 8;11;15;20;27;36.
>
> How about 0-15, also 30 on a $XXXX XXX1 alignment is an interesting worst
case.
>
> BTW: see my other message.  If we split the move problem into two pieces
it would significantly simplify the benchmarking.
>
>
> > But ok i will set up a benchmark including more small ones and the
overlapped case.
> > Does anybody have a good example of an overlapped move from real life?
>
> How about deleting an element from an array?
>
> --
> Bob Lee
> High Performance Delphi - http://www.optimalcode.com/
>
>
Re: Move Optimization
#126
Author: Dennis
Date: Fri, 20 Sep 2002 19:15
123 lines
4769 bytes
One more benchmark proposal. Small strings 1,2,3....8 byte. Random compiler
controlled alignment.
Longer strings get less runs -> all string length's attribute approximately
the same to the benchmark.

function StringMoves : Extended;
var
 SourceString1, DestString1 : AnsiString;
 RunTimeSec : Extended;//Destroy alignment
 SourceString2, DestString2 : AnsiString;
 AverageCopySpeed : Extended;//Destroy alignment
 SourceString3, DestString3 : AnsiString;
 SourceString4, DestString4 : AnsiString;
 MBMoved : Extended;//Destroy alignment
 SourceString5, DestString5 : AnsiString;
 SourceString6, DestString6 : AnsiString;
 SourceString7, DestString7 : AnsiString;
 SourceString8, DestString8 : AnsiString;
 I1, I2, I3, I4, I5, I6, I7, I8 : Cardinal;
 Count1, Count2, Count3, Count4, Count5, Count6, Count7, Count8 : Cardinal;
 lpFrequency, lpPerformanceCount, StartCount, EndCount, NoOfTicks :
TLargeInteger;
 Succes : Boolean;
const
 NOOFRUNS1 : Cardinal = 10000000;
 NOOFRUNS2 : Cardinal = 5000000;
 NOOFRUNS3 : Cardinal = 3333333;
 NOOFRUNS4 : Cardinal = 2500000;
 NOOFRUNS5 : Cardinal = 2000000;
 NOOFRUNS6 : Cardinal = 1666666;
 NOOFRUNS7 : Cardinal = 1400000;
 NOOFRUNS8 : Cardinal = 1250000;

begin
 MainForm.StatusBar1.SimpleText := 'Running Sub Benchmark: StringMoves';
 SourceString1 := 'A';
 DestString1   := '1';
 SourceString2 := 'AB';
 DestString2   := '01';
 SourceString3 := 'ABC';
 DestString3   := '012';
 SourceString4 := 'ABCD';
 DestString4   := '0123';
 SourceString5 := 'ABCDE';
 DestString5   := '01234';
 SourceString6 := 'ABCDEF';
 DestString6   := '012345';
 SourceString7 := 'ABCDEFG';
 DestString7   := '0123456';
 SourceString8 := 'ABCDEFGH';
 DestString8   := '01234567';
 Count1 := Length(SourceString1);
 Count2 := Length(SourceString2);
 Count3 := Length(SourceString3);
 Count4 := Length(SourceString4);
 Count5 := Length(SourceString5);
 Count6 := Length(SourceString6);
 Count7 := Length(SourceString7);
 Count8 := Length(SourceString8);
 Succes := QueryPerformanceFrequency(lpFrequency);
 if Succes then
  begin
   Succes := QueryPerformanceCounter(lpPerformanceCount);
   if Succes then
    StartCount := lpPerformanceCount
   else
    raise Exception.Create('QueryPerformanceCounter failed');
  end
 else
  raise Exception.Create('QueryPerformanceFrequency failed');
 for I1 := 1 to NoOfRuns1 do
  begin
   Move(SourceString1[1], DestString1[1], Count1);
  end;
 for I2 := 1 to NoOfRuns2 do
  begin
   Move(SourceString2[1], DestString2[1], Count2);
  end;
 for I3 := 1 to NoOfRuns3 do
  begin
   Move(SourceString3[1], DestString3[1], Count3);
  end;
 for I4 := 1 to NoOfRuns4 do
  begin
   Move(SourceString4[1], DestString4[1], Count4);
  end;
 for I5 := 1 to NoOfRuns5 do
  begin
   Move(SourceString5[1], DestString5[1], Count5);
  end;
 for I6 := 1 to NoOfRuns6 do
  begin
   Move(SourceString6[1], DestString6[1], Count6);
  end;
 for I7 := 1 to NoOfRuns7 do
  begin
   Move(SourceString7[1], DestString7[1], Count7);
  end;
 for I8 := 1 to NoOfRuns8 do
  begin
   Move(SourceString8[1], DestString8[1], Count8);
  end;
 Succes := QueryPerformanceCounter(lpPerformanceCount);
 if Succes then
  EndCount := lpPerformanceCount
 else
  raise Exception.Create('QueryPerformanceCounter failed');
 NoOfTicks := EndCount - StartCount;
 RunTimeSec := NoOfTicks / lpFrequency;
 MBMoved := (Count1 * NOOFRUNS1
           + Count2 * NOOFRUNS2
           + Count3 * NOOFRUNS3
           + Count4 * NOOFRUNS4
           + Count5 * NOOFRUNS5
           + Count6 * NOOFRUNS6
           + Count7 * NOOFRUNS7
           + Count8 * NOOFRUNS8) / (1024 * 1024);
 AverageCopySpeed := MBMoved / RunTimeSec;
 MainForm.RunTimeEdit.Text := FloatToStrF(RunTimeSec, ffFixed, 9, 1);
 Result := AverageCopySpeed;
end;

Dennis
Re: Move Optimization
#128
Author: Robert Lee
Date: Sat, 21 Sep 2002 00:28
20 lines
2173 bytes

Hagen Reddmann wrote:

> How about to collect first some realtime inputs of Move() calls or GetMem()
> and so on.

With functions like these there really aren't any "typical" apps.  You essentially have to optimize for all scenarios because any single app is likely to only use a small subset of the total and of those any one could be "the" time critical one.  This means you have to pursue differing strategies based on the input.  Then you have to come up with wost-case scenarios based on the algorithm and use those to make decisions.


> Why should be an high optimized MMX/SSE2 move() that requiere 1Mb moves,
> faster when I do it in ten years 1000 times, and 1 trillion times i do moves
> of length smaller than 64 bytes ?!

Like I said above you have to optimize for all the cases.  Fortunately, with move the optimization criteria shift with the input cases.  (minimal overhead for tiny moves -> minimal loop times for big moves)


--
Bob Lee
High Performance Delphi - http://www.optimalcode.com/
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads