benchmark sorts

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

benchmark sorts

Post by dafhi »

Code: Select all

/' benchmark sorts - 2021 Sep 4 - by dafhi

  2 fast quicksorts compared
   
   project features:
  
  sort validation
  
'/

#define def         #define
#undef int

def int         as Integer
def sng         as single
def dbl         as double


Type v3
  int         x,y,z
  'As double         x,y,z
End Type

type my_sort_type as v3
def dot         .z


'' normally pushed to stack (which can be mildly faster)
Dim shared int              j, i
dim shared as my_sort_type  x, tmp, pivot

 
def direction <

#macro ifswap(x,y)
  if a(y)dot direction a(x)dot then
    swap a(x),a(y)
    'tmp=a(x) : a(x)=a(y) : a(y)=tmp
  endif
#EndMacro
  


#macro SetQsort3(datatype,fname,dot) '' dodicat's macro concept
    Sub fname(a() as datatype, L int, r int)
      
      /' -- quicksort by dafhi - 2020 Jan 26
            
         fast, but sorted lists give O(n^2)
         suggest pre-shuffle:
        for i in list step 3:  swap a(i), a(random element)
      '/
      
      ifswap(L,r) '' L becomes pivot
      if L < r - 1 then
        var j=r, i=L '' shared var i
        whiLe i < j
          j -= 1: whiLe a(L)dot direction a(j)dot: j-=1: wend
          i += 1: while a(i)dot direction a(L)dot: i+=1: wend
          if j<=i then i=j: exit while
          tmp=a(i) : a(i)=a(j) : a(j)=tmp
        Wend
        ifswap(L,i)
        i -= 1:  if L < i then fname a(), L, i
        j += 1:  if j < r then fname a(), j, r
      endif
    End Sub
#EndMacro


#macro SetQsort(datatype,fname,dot)
    Sub fname(a() As datatype,begin As Long,Finish As Long)
    Dim As Long i=begin,j=finish
    'Dim As datatype x =a((I+J)\2)
    x = a((I+J)\2)
    While  I <= J
      While a(I)dot direction X dot:I+=1:Wend
      While x dot direction a(J)dot:J-=1:Wend
      If I<=J Then tmp=a(i) : a(i)=a(j) : a(j)=tmp: I+=1:J-=1
    wend
    'if a(j) dot = x dot then j-=1: i+=1
    If J > begin Then fname(a(),begin,J)
    If I < Finish Then fname(a(),I,Finish)
End Sub
#endmacro


#macro SetQsortp(datatype,fname,dot)
Sub fname(a() As datatype,l int,r int)
  'Dim As datatype x = a( (l+r)\2 )
  x = a( (l+r)\2 )
  var i=l, j=r
  While j > i
    While a(i)dot direction x dot:i +=1:Wend
    While x dot direction a(j)dot:j-=1:Wend
    if i>j then exit while 
    tmp=a(i) : a(i)=a(j) : a(j)=tmp
    i+=1:j-=1
  Wend
  If j > l  Then fname a(),l,j 
  If i  < r Then fname a(),i,r
End Sub
#endmacro

#macro SetQsort_nSquaredResist(datatype,fname,dot) '' dodicat's macro system
  Sub fname(a() as datatype, L int, r int)
    
    '' n^2 resistant & faster than average well-balanced quicksort
    
    var j=(l+r)\2
    ifswap(j,r)
    ifswap(L,r) '' L becomes pivot
    ifswap(j,l)
    if L < r - 1 then
      j=r: i=L '' shared var i
      whiLe i < j
        j -= 1: whiLe a(L)dot direction a(j)dot: j-=1: wend
        i += 1: while a(i)dot direction a(L)dot: i+=1: wend
        if j<=i then i=j: exit while
        tmp=a(i) : a(i)=a(j) : a(j)=tmp
      Wend
      ifswap(L,i)
      i -= 1:  if L < i then fname a(), L, i
      j += 1:  if j < r then fname a(), j, r
    endif
  End Sub
#endmacro



setqsort( my_sort_type, q, dot )
setqsort3( my_sort_type, q3, dot )
setqsortp( my_sort_type, qp, dot )


def flr(x) (((x)*2.0-0.5)shr 1) '' Stonemonkey's floor()

' ------- timing ---------------------
'

Const                 Elements = 49999

dim shared as long    ub_times = 3
dim shared as my_sort_type  ga(any)


setqsort_nSquaredResist( my_sort_type, qs_n2Resist, dot )


sub RandomData(a() as my_sort_type)
  
  var c = ubound(a) + 1
  
  '' generate some values
  for i int = 1 to c-1:  a(i)dot = rnd:  Next
  'for i int = 1 to c-1:  a(i)dot = a(i-1)dot + flr(rnd+.5):  Next
  
  '' sort using a known correct
  q a(), 0, c-1
  
  '' copy for sort validation
  for i int = 0 to c-1:  ga(i) = a(i):  next
  
  '' shuffle
  for i int = 0 to c-1 step 3:  swap a(i), a(flr(rnd*c)):  Next

End Sub


function Sorted(a() as my_sort_type) as boolean
  var correct = true, b = a(0)dot
  for i int = 0 to ubound(a)
    if a(i)dot <> ga(i)dot then correct = false: exit for
  Next
  return correct
end function


type tTimings
  as long           ub = -1
  dbl               a(any)
  as string         mesg
  declare operator  cast as string
  declare operator  cast dbl
  declare operator  cast sng
End Type
operator tTimings.cast as string:  return str(a(ub/2))
End Operator
operator tTimings.cast dbl:  return a(ub/2)
End Operator
operator tTimings.cast sng:  return a(ub/2)
End Operator


sub Sort_Times(A() As double, UB int = -1, LB int = 0) '' an insertion sort
  if lb>ub then lb=lbound(a): ub=ubound(a)
  
  var lo=lb
  for i int=lb+1 to ub
    if a(i) < a(lo) then lo=i
  next
  swap a(lb), a(lo)
  
  For i int=lb+1 To ub-1
    dim int j=i+1
    if a(j) < a(i) then
      dim dbl sw=a(j): j=i
      while sw<a(j): a(j+1)=a(j): j-=1: wend
      a(j+1)=sw
    endif
  Next
End Sub


dim shared dbl  times(ub_times)


#Macro mac_timer(algo, ret, algorithm_name)
  
    ret.mesg = algorithm_name & " "
    for i int = 0 to ub_times
      RandomData a()
      var t = timer
        algo
      times(i) = timer - t
      If not Sorted( a() ) then ? "sort error! "; ret.mesg
    Next
    
    '' outliers may skew average
    Sort_Times times()
    ret.ub+=1
    redim preserve ret.a(ret.ub)
    ret.a(ret.ub) = times(ub_times/2)
    Sort_Times ret.a()

#EndMacro

'
' --------------- timing mechanism


function round(in sng, places as ubyte = 2) as string
  dim int mul = 10 ^ places
  return str(csng(flr(in * mul + .5) / mul))
End Function


sub Main
 
  dim int             ub = Elements - 1
  
  dim as my_sort_type a(ub)
  redim               ga(ub) '' original list for for verification
  
  dim as tTimings     tA, tB
 
  #if 1
    'def sort_a mac_timer( q2( a(), 0, ubound(a) ), tA, "q2" )
    def sort_a mac_timer( q( a(), 0, ubound(a) ), tA, "q" )
  #else
    def sort_b mac_timer( qs_n2resist( a(), 0, ubound(a) ), tB, "n2 resist" )
  #endif
  
  #if 1
    def sort_b mac_timer( q3( a(), 0, ubound(a) ), tB, "q3" )
    'def sort_b mac_timer( q( a(), 0, ubound(a) ), tB, "q" )
  #elseif 0
    def sort_b mac_timer( q2( a(), 0, ubound(a) ), tB, "q2" )
  #else
    'def sort_b mac_timer( qp( a(), 0, ubound(a) ), tB, "qptr" )
  #endif
  
  sleep 250
  randomize
 
  ? " sorting .."

  for i int = 1 to 29
    if i mod 2=0 then sleep 1
    if rnd<.5 then
      sort_a 'order can make a difference
      sort_b
    else
      sort_b
      sort_a
    endif
  next
  cls
 
  var s = 0f, mesg = " "
  if tA<tB then
    s = tA / tB: mesg = tA.mesg
  else
    s = tB / tA: mesg = tB.mesg
  EndIf
  ?
  ? " winner:  "; mesg
  ?
  ? s; " .. "; round(1 / s, 4); "x"

  sleep
end sub

Main
Last edited by dafhi on Sep 04, 2021 6:51, edited 22 times in total.
paul doe
Moderator
Posts: 1732
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: benchmark sorts

Post by paul doe »

Nice! Very useful, thanks.
You have two warnings in the code:

Code: Select all

FbTemp.bas(219) warning 3(1): Passing different pointer types, at parameter 1 (algo) of PARTITION()
FbTemp.bas(221) warning 3(1): Passing different pointer types, at parameter 1 (algo) of PARTITION()
The last parameter of both signatures should be the same, either both long, or both integer, for FB to stop complaining ;)

Code: Select all

'' this is the comb_sort signature
Sub comb_sort(A() As mysorttype, lb As integer=0, ub As integer=0, k as long=1)

'' and this is the ytQS2 signature
sub ytQS2(a() as mysorttype, lb as integer=0, ub as integer=0, q as integer=1)
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: benchmark sorts

Post by srvaldez »

on my Mac with optimize level -Ofast

Code: Select all

 winner:  ytQS2  

 0.5809945 .. 1.72x
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: benchmark sorts

Post by jj2007 »

dafhi wrote:update 2 - adjusted timing window

Code: Select all

  var s = 0f, mesg = ""
  if tA<tB then
    s = tA / tB: mesg = tA.mesg
  else
    s = tB / tA: mesg = tB.mesg
  EndIf
I get some errors here for s=...:

Code: Select all

\TmpFile.bas(230) error 24: Invalid data types
J:\AllBasics\FreeBasic\tmp\TmpFile.bas(232) error 180: Invalid assignment/conversion
J:\AllBasics\FreeBasic\tmp\TmpFile.bas(234) error 180: Invalid assignment/conversion
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: benchmark sorts

Post by srvaldez »

no problems here, tested with FB versions 1.04, 1.05 and 1.06 both 32 and 64 bit, what version are you using?
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: benchmark sorts

Post by MrSwiss »

Code: Select all

function Sorted(a() as MySortType) as uinteger
  var b = a(0)dot
  for p as MySortType ptr = @a(1) to @a(ubound(a))
    if b b2 p->z then return FALSE
    b = p->z
  Next: return TRUE
end function
Seeing the return type: uinteger (which size? FBC 32 = ULong, FBC 64 = ULongInt) and,
return TRUE -- ???
IMHO, a return TRUE is only correct, with a Boolean type (return parameter)!

While a signed int. (any size) might be acceptable, any unsigned int. should at least,
trow a: WARNING: "incompatible variable assignment" ... (trying to assign: -1).
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: benchmark sorts

Post by dafhi »

jj2007 - hopefully MrSwiss caught the error (which I fixed)

srvaldez - I use O 3
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: benchmark sorts

Post by jj2007 »

srvaldez wrote:no problems here, tested with FB versions 1.04, 1.05 and 1.06 both 32 and 64 bit, what version are you using?
FreeBASIC Compiler - Version 1.05.0 (01-31-2016), built for win32 (32bit)
Copyright (C) 2004-2016 The FreeBASIC development team.
standalone

Code: Select all

  var s = 0f, mesg = ""
  if tA<tB then
    s = tA / tB: mesg = tA.mesg
  else
    s = tB / tA: mesg = tB.mesg
  EndIf

Code: Select all

TmpFile.bas(232) error 24: Invalid data types
TmpFile.bas(234) error 180: Invalid assignment/conversion
TmpFile.bas(236) error 180: Invalid assignment/conversion
The culprit is mesg; it works with

Code: Select all

  dim mesg as string=""
  var s = 0f

Code: Select all

 winner:  ytQS2
 0.7036515 .. 1.42x
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: benchmark sorts

Post by dafhi »

updated with Munair's quicksort
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: benchmark sorts

Post by dodicat »

Here is how I generalise the standard quicksort.

Code: Select all


'=========================================
#define up <,>
#define down >,<
#macro SetQsort(datatype,fname,b1,b2,dot)
Sub fname(array() As datatype,begin As Long,Finish As Ulong)
    Dim As Long i=begin,j=finish 
    Dim As datatype x =array(((I+J)\2))
    While  I <= J
        While array(I)dot b1 X dot:I+=1:Wend
            While array(J)dot b2 X dot:J-=1:Wend
                If I<=J Then Swap array(I),array(J): I+=1:J-=1
            Wend
            If J > begin Then fname(array(),begin,J)
            If I < Finish Then fname(array(),I,Finish)
        End Sub
        #endmacro 
 '===========================================       
        #macro printout(a)
        For n As Long=Lbound(a) To Ubound(a)
            #if typeof(a)<>udt
            Print n, a(n)
            #else
            Print n,a(n).x,a(n).y,a(n).z,a(n).s
            #endif
        Next
        Print
        #endmacro
        
        
        randomize
        'set up required sorts  
        SetQsort(Integer,sortintegerup,up,)
        SetQsort(Double,sortdoubledown,down,)
        SetQsort(String,sortstringup,up,)
        
        Type udt
            As single x,y,z
            as string * 2 s
        End Type
        
        SetQsort(udt,sortudtZup,up,.z)
  '----------------------------------      
        Dim As Integer i(3 To 9)
        For n As Long=3 To 9
            i(n)=Rnd*20
        Next
        sortintegerup(i(),3,9)
        printout(i)
        
        Dim As Double j(5)
        For n As Long=0 To 5
            j(n)=Rnd*20
        Next
        sortdoubledown(j(),0,5)
        printout(j) 
        
        Dim As String k(...)={"Free","Commercial","Students","Expensive"}
        sortstringup(k(),Lbound(k), Ubound(k))
        printout(k)
        
        
        Dim As udt z(1 To 4)
        For n As Long=1 To 4
            z(n)=Type(Rnd*10,Rnd*10,Rnd*10,str(n))
        Next
        
        printout(z)
        sortudtZup(z(),1,4)
        printout(z)
       
        Sleep
        
        
        
          
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: benchmark sorts

Post by D.J.Peters »

@dodicat here are how you can make your macro faster for free

B>A is faster than A<=B
DON'T use SWAP
use the native INTEGER for array index and loop counters (32 vs 64-bit)
...

Joshy

Code: Select all

#macro SetQsort(datatype,fname,b1,b2,dot)
Sub fname(array() As datatype,begin As integer,Finish As integer)
  Dim As integer iLeft=begin,iRight=finish
  Dim As datatype tmp,x = array(((begin+finish)\2))
  While iRight>iLeft ' !!! I <= J
    While array(iLeft )dot b1 x dot:iLeft +=1:Wend
    While array(iRight)dot b2 x dot:iRight-=1:Wend
    if iLeft>iRight then exit while  
    ' !!! If I<=J Then
      ' !!! Swap array(I),array(J)
    tmp=array(iLeft) : array(iLeft)=array(iRight) : array(iRight)=tmp
    iLeft+=1:iRight-=1
    ' !!! end if  
  Wend
  If iRight > begin  Then fname(array(),begin,iRight)
  If iLeft  < Finish Then fname(array(),iLeft,Finish)
End Sub
#endmacro
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: benchmark sorts

Post by dodicat »

Thanks D.J.Peters.
Your method is faster when using optimised gcc.
Also faster when using -gen gas.
Unoptimised gcc seems to be slower on 32 bits and about the same on 64 bits.
Swap is slower -agreed.
Integer, even on 64 bits is faster than long -- agreed.

10 runs for each method.

Code: Select all

 

#define up <,>
#define down >,<
#macro SetQsort(datatype,fname,b1,b2,dot)
Sub fname(array() As datatype,begin As integer,Finish As integer)
  Dim As integer iLeft=begin,iRight=finish
  Dim As datatype  x = array(((begin+finish)\2))
  dim as datatype tmp
  While iRight>iLeft ' !!! I <= J
    While array(iLeft )dot b1 x dot:iLeft +=1:Wend
    While array(iRight)dot b2 x dot:iRight-=1:Wend
    if iLeft>iRight then exit while  
    ' !!! If I<=J Then
      ' !!! Swap array(I),array(J)
      ''swap array(iLeft),array(iright) 'slow
    tmp=array(iLeft) : array(iLeft)=array(iRight) : array(iRight)=tmp
    iLeft+=1:iRight-=1
    ' !!! end if  
  Wend
  If iRight > begin  Then fname(array(),begin,iRight)
  If iLeft  < Finish Then fname(array(),iLeft,Finish)
End Sub
#endmacro

#macro SetQsort2(datatype,fname,b1,b2,dot)
Sub fname(array() As datatype,begin As integer,Finish As integer)
    Dim As integer i=begin,j=finish 
    Dim As datatype x =array(((I+J)\2))
    While  I <= J
        While array(I)dot b1 X dot:I+=1:Wend
            While array(J)dot b2 X dot:J-=1:Wend
                If I<=J Then Swap array(I),array(J): I+=1:J-=1
            Wend
            If J > begin Then fname(array(),begin,J)
            If I < Finish Then fname(array(),I,Finish)
        End Sub
        #endmacro 
        


type v3
    as single x,y,z
end type

dim as double addtimes

setqsort2(v3,sortz2,down,.z)
setqsort(v3,sortz,down,.z)


sub set(x() as v3)
    randomize 1
redim  x(5000000)
for n as long=0 to ubound(x)
    x(n)=type(rnd,rnd,rnd)
next
end sub

redim as v3 x()

set(x())
sortz2(x(),0,ubound(x))  'warm up

print "original"
for n as long=1 to 10
set(x())
dim as double t=timer,t2
sortz2(x(),0,ubound(x))
t2=timer
addtimes+=t2-t
print t2-t
next n
print
print "total + check value "; addtimes,x(111222).z
sleep 50
print
print
addtimes=0
print "D.J.Peters"
for n as long=1 to 10
set(x())
dim as double t=timer,t2
sortz(x(),0,ubound(x))
t2=timer
addtimes+=t2-t
print t2-t
next n
print
print "total + check value "; addtimes,x(111222).z
sleep

 
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: benchmark sorts

Post by D.J.Peters »

@dafhi why do you ignore the FreeBASIC pointer quick sort in your benchmark ?

Joshy
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: benchmark sorts

Post by dafhi »

D. J. Peters i'm having trouble getting the sub to work
Post Reply