Wilbert is surprised about how something that looks more complex can be faster than the concatenate operator + (and knows about the workarounds). So possibly there are some easy improvements to be made under the hood? We know there are faster methods (but it should be transparent, JavaScript does it too behind the scenes, no need for stringbuilder etc). Anyway, mk-soft, thanks again for that nice code.
For what its worth:
Code: Select all
EnableExplicit
Define l
Define b.s = "..."
Define a.s = "hello"
a + b
a + b
Debug a
a.s = "hello"
l = Len(a)
a = LSet(a, l + Len(b))
PokeS(@a + l * SizeOf(Character), b)
l = Len(a)
a = LSet(a, l + Len(b))
PokeS(@a + l * SizeOf(Character), b)
Debug a
gives this ASM:
Code: Select all
;
; PureBasic 5.62 (Windows - x86) generated code
;
; (c) 2016 Fantaisie Software
;
; The header must remain intact for Re-Assembly
;
; String
; Memory
; :System
; kernel32.lib
; :Import
;
format MS COFF
;
;
extrn _PB_FreeMemorys@0
extrn _PB_InitMemory@0
extrn _PB_Len@4
extrn _PB_LSet@12
extrn _PB_PokeS@8
extrn _ExitProcess@4
extrn _GetModuleHandleW@4
extrn _HeapCreate@12
extrn _HeapDestroy@4
extrn _memset
extrn _SYS_CopyString@4
extrn _SYS_AllocateString4@8
extrn SYS_FastAllocateStringFree
extrn PB_StringBase
extrn _SYS_InitString@0
extrn _SYS_FreeStrings@0
;
extrn _PB_StringBasePosition
public _PB_Instance
public _PB_ExecutableType
public _PB_OpenGLSubsystem
public _PB_MemoryBase
public PB_Instance
public PB_MemoryBase
public _PB_EndFunctions
macro pb_public symbol
{
public _#symbol
public symbol
_#symbol:
symbol:
}
macro pb_align value { rb (value-1) - ($-_PB_DataSection + value-1) mod value }
macro pb_bssalign value { rb (value-1) - ($-_PB_BSSSection + value-1) mod value }
public PureBasicStart
;
section '.code' code readable executable align 4096
;
;
PureBasicStart:
;
PUSH dword I_BSSEnd-I_BSSStart
PUSH dword 0
PUSH dword I_BSSStart
CALL _memset
ADD esp,12
PUSH dword 0
CALL _GetModuleHandleW@4
MOV [_PB_Instance],eax
PUSH dword 0
PUSH dword 4096
PUSH dword 0
CALL _HeapCreate@12
MOV [PB_MemoryBase],eax
CALL _SYS_InitString@0
CALL _PB_InitMemory@0
; EnableExplicit
;
; Define l
; Define b.s = "..."
MOV edx,_S1
LEA ecx,[v_b]
CALL SYS_FastAllocateStringFree
; Define a.s = "hello"
MOV edx,_S2
LEA ecx,[v_a]
CALL SYS_FastAllocateStringFree
;
; a + b
MOV edx,dword [v_a]
PUSH dword [_PB_StringBasePosition]
PUSH edx
CALL _SYS_CopyString@4
MOV edx,dword [v_b]
PUSH edx
CALL _SYS_CopyString@4
PUSH dword v_a
CALL _SYS_AllocateString4@8
; a + b
MOV edx,dword [v_a]
PUSH dword [_PB_StringBasePosition]
PUSH edx
CALL _SYS_CopyString@4
MOV edx,dword [v_b]
PUSH edx
CALL _SYS_CopyString@4
PUSH dword v_a
CALL _SYS_AllocateString4@8
; Debug a
;
; a.s = "hello"
MOV edx,_S2
LEA ecx,[v_a]
CALL SYS_FastAllocateStringFree
; l = Len(a)
PUSH dword [v_a]
CALL _PB_Len@4
MOV dword [v_l],eax
; a = LSet(a, l + Len(b))
MOV edx,[_PB_StringBasePosition]
PUSH edx
PUSH edx
MOV ebx,dword [v_l]
PUSH dword [v_b]
CALL _PB_Len@4
ADD ebx,eax
PUSH ebx
PUSH dword [v_a]
CALL _PB_LSet@12
PUSH dword v_a
CALL _SYS_AllocateString4@8
; PokeS(@a + l * SizeOf(Character), b)
PUSH dword [v_b]
MOV eax,dword [v_a]
MOV ebx,eax
MOV edi,dword [v_l]
ADD edi,edi
ADD ebx,edi
PUSH ebx
CALL _PB_PokeS@8
; l = Len(a)
PUSH dword [v_a]
CALL _PB_Len@4
MOV dword [v_l],eax
; a = LSet(a, l + Len(b))
MOV edx,[_PB_StringBasePosition]
PUSH edx
PUSH edx
MOV ebx,dword [v_l]
PUSH dword [v_b]
CALL _PB_Len@4
ADD ebx,eax
PUSH ebx
PUSH dword [v_a]
CALL _PB_LSet@12
PUSH dword v_a
CALL _SYS_AllocateString4@8
; PokeS(@a + l * SizeOf(Character), b)
PUSH dword [v_b]
MOV eax,dword [v_a]
MOV ebx,eax
MOV edi,dword [v_l]
ADD edi,edi
ADD ebx,edi
PUSH ebx
CALL _PB_PokeS@8
;
; Debug a
_PB_EOP_NoValue:
PUSH dword 0
_PB_EOP:
CALL _PB_EndFunctions
CALL _SYS_FreeStrings@0
PUSH dword [PB_MemoryBase]
CALL _HeapDestroy@4
CALL _ExitProcess@4
_PB_EndFunctions:
CALL _PB_FreeMemorys@0
RET
;
;
section '.data' data readable writeable
;
_PB_DataSection:
_PB_OpenGLSubsystem: db 0
pb_public PB_DEBUGGER_LineNumber
dd -1
pb_public PB_DEBUGGER_IncludedFiles
dd 0
pb_public PB_DEBUGGER_FileName
db 0
pb_public PB_Compiler_Unicode
dd 1
pb_public PB_Compiler_Thread
dd 0
pb_public PB_Compiler_Purifier
dd 0
pb_public PB_Compiler_Debugger
dd 0
_PB_ExecutableType: dd 0
align 4
public _SYS_StaticStringStart
_SYS_StaticStringStart:
_S1: dw 46,46,46,0
_S2: dw 104,101,108,108,111,0
pb_public PB_NullString
dw 0
public _SYS_StaticStringEnd
_SYS_StaticStringEnd:
align 4
align 4
align 4
s_s:
dd 0
dd -1
align 4
;
section '.bss' readable writeable
_PB_BSSSection:
align 4
;
I_BSSStart:
_PB_MemoryBase:
PB_MemoryBase: rd 1
_PB_Instance:
PB_Instance: rd 1
;
align 4
PB_DataPointer rd 1
v_a rd 1
v_b rd 1
v_l rd 1
align 4
align 4
align 4
align 4
I_BSSEnd:
section '.data' data readable writeable
SYS_EndDataSection:
Way over my head to analyze this ;
ASM of opening post:
Code: Select all
;
; PureBasic 5.62 (Windows - x86) generated code
;
; (c) 2016 Fantaisie Software
;
; The header must remain intact for Re-Assembly
;
; System
; String
; Requester
; FileSystem
; Date
; Object
; SimpleList
; Memory
; :System
; kernel32.lib
; :Import
;
format MS COFF
;
;
extrn _PB_ElapsedMilliseconds@0
extrn _PB_FreeFileSystem@0
extrn _PB_FreeMemorys@0
extrn _PB_FreeObjects@0
extrn _PB_InitMemory@0
extrn _PB_InitRequester@0
extrn _PB_Len@4
extrn _PB_LSet@12
extrn _PB_MessageRequester@8
extrn _PB_PokeS@8
extrn _PB_Str@12
extrn _ExitProcess@4
extrn _GetModuleHandleW@4
extrn _HeapCreate@12
extrn _HeapDestroy@4
extrn _memset
extrn _SYS_CopyString@4
extrn _SYS_AllocateString4@8
extrn SYS_FastAllocateStringFree
extrn PB_StringBase
extrn _SYS_InitString@0
extrn _SYS_FreeStrings@0
;
extrn _PB_StringBasePosition
public _PB_Instance
public _PB_ExecutableType
public _PB_OpenGLSubsystem
public _PB_MemoryBase
public PB_Instance
public PB_MemoryBase
public _PB_EndFunctions
macro pb_public symbol
{
public _#symbol
public symbol
_#symbol:
symbol:
}
macro pb_align value { rb (value-1) - ($-_PB_DataSection + value-1) mod value }
macro pb_bssalign value { rb (value-1) - ($-_PB_BSSSection + value-1) mod value }
public PureBasicStart
;
section '.code' code readable executable align 4096
;
;
PureBasicStart:
;
PUSH dword I_BSSEnd-I_BSSStart
PUSH dword 0
PUSH dword I_BSSStart
CALL _memset
ADD esp,12
PUSH dword 0
CALL _GetModuleHandleW@4
MOV [_PB_Instance],eax
PUSH dword 0
PUSH dword 4096
PUSH dword 0
CALL _HeapCreate@12
MOV [PB_MemoryBase],eax
CALL _SYS_InitString@0
CALL _PB_InitMemory@0
CALL _PB_InitRequester@0
; DisableDebugger
;
; b.s = "..."
MOV edx,_S1
LEA ecx,[v_b]
CALL SYS_FastAllocateStringFree
; a.s = "hello"
MOV edx,_S2
LEA ecx,[v_a]
CALL SYS_FastAllocateStringFree
;
; t1 = ElapsedMilliseconds()
CALL _PB_ElapsedMilliseconds@0
MOV dword [v_t1],eax
; For i = 1 To 10000
MOV dword [v_i],1
JMP _ForSkipDebug1
_For1:
_ForSkipDebug1:
MOV eax,10000
CMP eax,dword [v_i]
JL _Next2
; a + b
MOV edx,dword [v_a]
PUSH dword [_PB_StringBasePosition]
PUSH edx
CALL _SYS_CopyString@4
MOV edx,dword [v_b]
PUSH edx
CALL _SYS_CopyString@4
PUSH dword v_a
CALL _SYS_AllocateString4@8
; Next
_NextContinue2:
INC dword [v_i]
JNO _For1
_Next2:
; t2 = ElapsedMilliseconds()
CALL _PB_ElapsedMilliseconds@0
MOV dword [v_t2],eax
;
; a.s = "hello"
MOV edx,_S2
LEA ecx,[v_a]
CALL SYS_FastAllocateStringFree
;
; t3 = ElapsedMilliseconds()
CALL _PB_ElapsedMilliseconds@0
MOV dword [v_t3],eax
; For i = 1 To 10000
MOV dword [v_i],1
JMP _ForSkipDebug3
_For3:
_ForSkipDebug3:
MOV eax,10000
CMP eax,dword [v_i]
JL _Next4
; l = Len(a)
PUSH dword [v_a]
CALL _PB_Len@4
MOV dword [v_l],eax
; a = LSet(a, l + Len(b))
MOV edx,[_PB_StringBasePosition]
PUSH edx
PUSH edx
MOV ebx,dword [v_l]
PUSH dword [v_b]
CALL _PB_Len@4
ADD ebx,eax
PUSH ebx
PUSH dword [v_a]
CALL _PB_LSet@12
PUSH dword v_a
CALL _SYS_AllocateString4@8
; PokeS(@a + l*SizeOf(Character), b)
PUSH dword [v_b]
MOV eax,dword [v_a]
MOV ebx,eax
MOV edi,dword [v_l]
ADD edi,edi
ADD ebx,edi
PUSH ebx
CALL _PB_PokeS@8
; Next
_NextContinue4:
INC dword [v_i]
JNO _For3
_Next4:
; t4 = ElapsedMilliseconds()
CALL _PB_ElapsedMilliseconds@0
MOV dword [v_t4],eax
;
; MessageRequester("Timings", Str(t2-t1)+" vs "+Str(t4-t3))
MOV edx,[_PB_StringBasePosition]
PUSH edx
PUSH edx
PUSH dword [_PB_StringBasePosition]
MOV eax,dword [v_t2]
CDQ
PUSH edx
PUSH eax
MOV eax,dword [v_t1]
CDQ
SUB [esp],eax
SBB [esp+4],edx
POP eax
POP edx
PUSH edx
PUSH eax
CALL _PB_Str@12
MOV edx,_S4
PUSH edx
CALL _SYS_CopyString@4
MOV edx,[_PB_StringBasePosition]
PUSH edx
PUSH edx
MOV eax,dword [v_t4]
CDQ
PUSH edx
PUSH eax
MOV eax,dword [v_t3]
CDQ
SUB [esp],eax
SBB [esp+4],edx
POP eax
POP edx
PUSH edx
PUSH eax
CALL _PB_Str@12
POP eax
ADD dword [_PB_StringBasePosition],2
MOV eax,_S3
PUSH eax
MOV edx,[PB_StringBase]
ADD [esp+4],edx
CALL _PB_MessageRequester@8
POP dword [_PB_StringBasePosition]
_PB_EOP_NoValue:
PUSH dword 0
_PB_EOP:
CALL _PB_EndFunctions
CALL _SYS_FreeStrings@0
PUSH dword [PB_MemoryBase]
CALL _HeapDestroy@4
CALL _ExitProcess@4
_PB_EndFunctions:
CALL _PB_FreeFileSystem@0
CALL _PB_FreeObjects@0
CALL _PB_FreeMemorys@0
RET
;
;
section '.data' data readable writeable
;
_PB_DataSection:
_PB_OpenGLSubsystem: db 0
pb_public PB_DEBUGGER_LineNumber
dd -1
pb_public PB_DEBUGGER_IncludedFiles
dd 0
pb_public PB_DEBUGGER_FileName
db 0
pb_public PB_Compiler_Unicode
dd 1
pb_public PB_Compiler_Thread
dd 0
pb_public PB_Compiler_Purifier
dd 0
pb_public PB_Compiler_Debugger
dd 0
_PB_ExecutableType: dd 0
align 4
public _SYS_StaticStringStart
_SYS_StaticStringStart:
_S1: dw 46,46,46,0
_S2: dw 104,101,108,108,111,0
_S4: dw 32,118,115,32,0
_S3: dw 84,105,109,105,110,103,115,0
pb_public PB_NullString
dw 0
public _SYS_StaticStringEnd
_SYS_StaticStringEnd:
align 4
align 4
align 4
s_s:
dd 0
dd -1
align 4
;
section '.bss' readable writeable
_PB_BSSSection:
align 4
;
I_BSSStart:
_PB_MemoryBase:
PB_MemoryBase: rd 1
_PB_Instance:
PB_Instance: rd 1
;
align 4
PB_DataPointer rd 1
v_a rd 1
v_b rd 1
v_i rd 1
v_l rd 1
v_t1 rd 1
v_t2 rd 1
v_t3 rd 1
v_t4 rd 1
align 4
align 4
align 4
align 4
I_BSSEnd:
section '.data' data readable writeable
SYS_EndDataSection: