[Back to SORTING SWAG index]  [Back to Main SWAG index]  [Original]

{I wrote a small Program to bench both sort routines we posted. It was an
interesting test, however it did raise a couple questions For me, which I'll
get to in a moment. (The following Program can be used as a skeleton For trying
other sort routines too.)

Needless to say, the routine you posted was dramatically faster than the one I
posted, even though both routines are non-recursive simple sorts.

The maximum efficient load of the routine you posted appears to be about 3000
elements. After that, additonal elements add time exponentially. For example,
it will sort 3000 elements in 5.1 seconds, but 5000 elements takes almost 16
seconds. The sort I posted became un-benchable [bearable] at about 3000
elements when it took over a minute to Complete. I didn't test it beyond this
point.

Here are the results from my 386 33Mhz machine-- your algorithm.

     500 Elements - 0.1   Seconds
    1000 Elements - 0.8   Seconds
    1500 Elements - 1.4   Seconds
    2000 Elements - 2.6   Seconds
    3000 Elements - 5.1   Seconds  <- Peak efficiency reached
    5000 Elements - 15.8  Seconds

Here is the Program I used to benchmark with. I made it so that you could
"tweak" portions of the sort and re-run the Program.

Incidentally, I also Compiled this Program under Stony Brook's Pascal Plus and
was suprised to find that it ran substantially slower. All optimizations on.

Range Checking ($R+) exactly Doubled the time it took to sort.

Changing "Span+1" to Succ(Span) and "total-1" to Pred(total) made the routine
about 3% faster. However the routine then neglected to sort that last two
elements. Adding "Inc(total,2)" solved the problem but I'm not sure why. I did
not expect this behavior. Perhaps someone could explain why?

I added a temporary Pointer Variable to your routine in place of the "NewStr('
...  ')" code you used to simplify it.

and one last thing... Using OPRO's OpInline Function called
"ExchangeLongInts()" to do the swapping instead of using a temporary Var
increased speed another 2% (Evident at > 2000 elements.) However I did not
include this so that anyone interested could Compile and run this without extra
Units.
}

{$A+,B-,D-,E-,F-,G-,I+,L+,N-,O-,P-,Q+,R-,S+,T-,V-,X-,Y+}
{$M 32768,0,655360}

Program Sort_Test;  { Sorting Benchmark Using P. Beeftink's Algorithm }

Type
   SmallArrPtr = ^SmallArr;
   SmallArr    = Array[1..10] of Char;   { Skip String & Length Byte }

   TTimeString = String[20];


Var
   SortArray : Array[1..5000] of SmallArrPtr; { A LARGE Array }

   TickCount : LongInt Absolute $0040:$006C;
 { TickCount : LongInt VOLATILE Absolute $0040:$006C; } { For Pascal+ }
   Tstart,
   Ttime     : LongInt;

{------------------------------------------------------------------------}
Procedure StartTiming;
begin
  TStart := TickCount;

  {start at the beginning of a tick!}
  Repeat Until TStart <> TickCount;

  TStart := TickCount;

end;
{------------------------------------------------------------------------}
Procedure StopTiming;
begin
  TTime := TickCount - TStart;
end;
{------------------------------------------------------------------------}
Function Elapsed : TTimeString;
Var Temp : TTimeString;
   Sec10 : LongInt;
begin

  Sec10 := TTime * 2470 div 4497;
  Str( Sec10 : 4, Temp );

  if Temp[1] = ' ' then Temp[1] := '0';

  Inc( Temp[0] );
  Temp[ Length(Temp) ] := Temp[ Pred( Length( Temp ) ) ];
  Temp[ Pred( length( Temp ) ) ] := '.';

  Elapsed := Temp;
end;
{------------------------------------------------------------------------}
Procedure MakeRandomStrings( NumtoMake : Word );
Var RNum,
    I,S  : Word;
    Temp : String;
begin

  Temp := '';
  Temp[0] := Chr( 10 );
  Randomize;

  For I := 1 to NumtoMake do
  begin

    For S := 1 to 10 do     { Create Random Strings 10 Chars in length }
    begin
      RNum := Random(26);
      Temp[S] := Chr( RNum + 65 );
    end;

    Move( Temp[1], SortArray[I]^, 10 );

  end;

end; { Proc }
{------------------------------------------------------------------------}
Procedure KDSort( total : Word );
  {-My simple sort routine as posted in Pascal Echo }
  { With 2 slight modifications                     }
Var
   i,j,
   Current : Word;
   TempPtr : Pointer;
begin

  For I := 1 to total do
  begin

    Current := I;

    For J := Succ(I) to total do
    begin
      if SortArray[J]^ < SortArray[Current]^ then
      begin
         TempPtr            := SortArray[j];
         SortArray[j]       := SortArray[Current];
         SortArray[Current] := TempPtr;
      end; {if}
    end; {For}

  end; {For}

end;
{------------------------------------------------------------------------}
Procedure PBSort(total : Integer);
  {-Peter Beeftink's Sort as Posted in Pascal Echo }
  { Also With slight modifications                 }
Var
   I,j     : Integer;
   Span    : Integer;
   TempPtr : Pointer;
begin

  Inc(total,2);   { Required to Compensate For PRED and SUCC ? }

  Span := total SHR $01;

  While Span > 0 do
  begin

    For I := Span to Pred(total) {total-1} do
    begin

      For j := (I - Succ(Span) {Span+1} ) Downto 1 do
        if (SortArray[j]^ <= SortArray[j+Span]^) then j := 1 else
        begin
          TempPtr           := SortArray[j];
          SortArray[j]      := SortArray[j+Span];
          SortArray[j+Span] := TempPtr;
        end;

    end; {For}

    Span := Span SHR $01; { This does help speed over Span div 2! }

  end; {WhIle}

end;
{------------------------------------------------------------------------}
Procedure Do_Sorting( SortAmount : Word );
begin

  MakeRandomStrings(SortAmount);

  Write('Sorting... ');

  StartTiming;

  PBSort(SortAmount); { Change to KDSort() to bench second sort routine }

  StopTiming;

  WriteLn(SortAmount:5,' Elements - ',Elapsed,' Seconds');

end;
{------------------------------------------------------------------------}
Var C : Word;

begin

  if MaxAvail < 5000 * Sizeof(SmallArr) then Halt; { not enough memory! }

  For C := 1 to 5000 do   { pre-allocate up front }
    GetMem(SortArray[C],Sizeof(SmallArr));


  Do_Sorting( 500   );   { Add more Do_Sorting()'s For whatever count }
  Do_Sorting( 1000  );   { you wish to test with.                     }
  Do_Sorting( 1500  );
  Do_Sorting( 2000  );
  Do_Sorting( 3000  );
  Do_Sorting( 5000  );


  { Un-comment the following if you wish to see the sorted output }

  {
  For C := 1 to 5000 do   { Change 5000 to the amount you sorted }
    WriteLn( SortArray[C]^ );


  For C := 1 to 5000 do
    FreeMem(SortArray[C],Sizeof(SmallArr));

end.
{
I plugged in a QuickSort algorithm in the "skeleton" Program in my previous
message to test perFormance. Here are the results:

     500 Elements - 0.1 Seconds
    1000 Elements - 0.2 Seconds
    1500 Elements - 0.4 Seconds
    2000 Elements - 0.6 Seconds
    3000 Elements - 0.9 Seconds
    5000 Elements - 1.8 Seconds

Very fast indeed. I modified the algorithm to sort only by Pointers, and
optimized a couple spots. Again, a slight speed increase is noted using OPRO's
ExchangeLongInts() in leu of using temporary Variables in 1 spot. if you have
OPRO, replace them and you reduce the number of instructions by 2 per
iteration.

This is a split-list recursive sort. Works by making a pass through the entire
Array first and moves all "small" data to the left, and all "Large" data to the
right. then it sorts each half seperately.

Take the following code segment and "plug" it into the skeleton in my previous
message. then change the "PBSort(SortAmount)" to "QuickSort(SortAmount)" to run
the tests.

Here is the code segment:

{------------------------------------------------------------------------}
Procedure QuickSort( total : Integer );
  {------------------------------------------}
  Procedure recQuickSort( L, R : Integer );
  Var K,I,J   : Integer;
      T,
      Temp    : Pointer;

  begin

    if L < R then
    begin
      T := SortArray[L];
      I := Pred(L);
      J := L;
      K := Succ(R);

      While Succ(J) < K do
       if SortArray[Succ(J)]^ < SmallArrPtr(T)^ then
       begin
         Inc(I,1);
         Inc(J,1);
         SortArray[I] := SortArray[J];
         SortArray[j] := T;
       end {if}
       else
       if SortArray[Succ(J)]^ > SmallArrPtr(T)^ then
       begin
         Dec(K,1);
         Temp := SortArray[K];
         SortArray[K] := SortArray[Succ(J)];
         SortArray[Succ(J)] := Temp;
       end {if}
       else
       Inc(J,1);

       recQuickSort(L,I);
       recQuickSort(K,R);

    end; { if L < R }

  end; { Proc recQuickSort }
  {------------------------------------------}

begin

  recQuickSort(1,total);

end;{QuickSort}
{------------------------------------------------------------------------}

[Back to SORTING SWAG index]  [Back to Main SWAG index]  [Original]