Code: Select all
! macro EndMacro {}
; Assembler macro to align code on a given boundary,
; aligning loop starts on 8 byte boundaries can speed up the code
; by about 1.5 to 2 times!
! macro calignjmp value
! {
!   local dest
!
!   if ((value - 1) - ((($ - $$) + value - 1) mod value)) > 3
!     makeDest equ 1
!     jmp dest
!   else
!     makeDest equ 0
!   end if
!
!   rept value
!   \{
!     if ($ - $$) mod value
!       nop
!     end if
!   \}
!   dest:
! } EndMacro
; Uncommend the next 3 lines, if you want to replace
; Mid with FMid in all places:
; Macro Mid(string, start, length)
;   FMid(@string,start,length)
; EndMacro
CompilerIf #PB_Compiler_Unicode
Procedure.s FMid(*srcPtr.Word, start.l, length.l)
  !MOV   edx,[p.p_srcPtr]
  !MOV   ecx,[p.v_length]
  !TEST  edx,edx
  !JZ    l_fmid_empty
  !TEST  ecx,ecx
  !JLE   l_fmid_empty
  !MOV   ecx,[p.v_start]
  !CMP   ecx,1
  !JLE   l_fmid_return
  !XOR   eax,eax
  !DEC   ecx
  !calignjmp 4
  fmid_scan:
  !CMP   [edx],ax
  !JE    l_fmid_scanend
  !ADD   edx,2
  !DEC   ecx
  !JNZ    l_fmid_scan
  fmid_scanend:
  !MOV   [p.p_srcPtr],edx
  fmid_return:
  ProcedureReturn PeekS(*srcPtr, length)
  fmid_empty:
  ProcedureReturn ""
EndProcedure
CompilerElse
Procedure.s FMid(*srcPtr.Byte, start.l, length.l)
  !MOV   edx,[p.p_srcPtr]
  !MOV   ecx,[p.v_length]
  !TEST  edx,edx
  !JZ    l_fmid_empty
  !TEST  ecx,ecx
  !JLE   l_fmid_empty
  !MOV   ecx,[p.v_start]
  !CMP   ecx,1
  !JLE   l_fmid_return
  !XOR   eax,eax
  !DEC   ecx
  !calignjmp 4
  fmid_scan:
  !CMP   [edx],al
  !JE    l_fmid_scanend
  !INC   edx
  !DEC   ecx
  !JNZ    l_fmid_scan
  fmid_scanend:
  !MOV   [p.p_srcPtr],edx
  fmid_return:
  ProcedureReturn PeekS(*srcPtr, length)
  fmid_empty:
  ProcedureReturn ""
EndProcedure
CompilerEndIf
Code: Select all
EnableExplicit
;IncludePath #PBIncludePath
XIncludeFile "FMid.pbi"
OpenConsole()
Global tStart.l, tStart2.l, tDiff.d
Global str1.s, str1Pos.l, str1Len.l = 16384, str1Mid.s
Global i.l, iEnd.l, strCh.l
strCh = 1
For i = 1 To str1Len
  str1 + Chr(strCh)
  strCh + 1
  If strCh > 255: strCh = 1: EndIf
Next i
iEnd = 10000000
str1Pos = 1
While str1Pos <= str1Len
  tStart2 = ElapsedMilliseconds(): Repeat: tStart = ElapsedMilliseconds(): Until tStart <> tStart2
  For i = 1 To iEnd
    str1Mid = Mid(str1, str1Pos, 1)
  Next i
  tDiff = (ElapsedMilliseconds() - tStart) / 1000.0
  If tDiff >= 1: iEnd / tDiff: EndIf
  PrintN(Right("      " + Str(str1Pos), 7) + ": " + StrD(tDiff, 3) + " sec., Ops per sec.: " + StrD(iEnd / tDiff, 3) + "    " + Str(Asc(str1Mid)))
  str1Pos + str1Pos
Wend
PrintN("")
iEnd = 1000000
str1Pos = 1
While str1Pos <= str1Len
  tStart2 = ElapsedMilliseconds(): Repeat: tStart = ElapsedMilliseconds(): Until tStart <> tStart2
  For i = 1 To iEnd
    str1Mid = FMid(@str1, str1Pos, 1)
  Next i
  tDiff = (ElapsedMilliseconds() - tStart) / 1000.0
  If str1Mid <> Mid(str1, str1Pos, 1): PrintN("Error"): EndIf
  If tDiff >= 1: iEnd / tDiff: EndIf
  PrintN(Right("      " + Str(str1Pos), 7) + ": " + StrD(tDiff, 3) + " sec., Ops per sec.: " + StrD(iEnd / tDiff, 3) + "    " + Str(Asc(str1Mid)))
  str1Pos + str1Pos
Wend
PrintN("")
PrintN(#CRLF$ + "Done")
While Inkey() = "": Delay(100): Wend
but FMid() is even faster on extracting from the start of a string,
for longer string the speed gain is between 2-3 times for positions above
64 for ascii strings, and about 1.5 - 2 for unicode strings.

