Hmm, good point! Normally you would.
But I can imagine situations where you would need to physically swap the memory contents.
And when it comes to swapping partial memory then it's not possible to do so by simple pointer swapping.
Here is a comparison between two PureBasic and two ASM implementations of a MemorySwap()
Please note that these tests just swap entire memory PureBasic allocations and they must match in length.
Further improvements would be to modify it so it'll work with any memory (PB allocated or not), and maybe some optimization in the looping etc.
These are the test results on my system using size=1024*1024*910 (Which x2 equals around 1.8GB memory total)
Processor=x64, T1=2389ms, T2=2604ms, T3=6406ms, T4=1400ms.
Processor=x86, T1=2243ms, T2=2575ms, T3=6453ms, T4=1220ms.
I was rather surprised myself, it seems the PureBasic team has some very CPU cache friendly Swap code.
Note that this was on a AMD Phenom X3, how does it behave on Core 2 or i7 ?
Oh and I did an extra test on x64 using size=1024*1024*1500
which totals to 3GB, something not possible using x86.
Processor=x64, T1=3963ms, T2=4344ms, T3=10656ms, T4=2335ms.
Wow! Using Quads and Swap seems to really fly on large memory amounts.
NOTE! Only the "native" MemorySwap() and MemorySwap2() works fully on x64, the asm variants will probably choke on x64 systems with more than 4GB mem where the referenced memory is above the 4GB range.
Code: Select all
EnableExplicit
CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
#Processor=64
CompilerElse
#Processor=86
CompilerEndIf
Procedure.i MemorySwapASM2(*src,*dst) ;Swap PB allocated memory, size must be the same.
Protected result.i=#False,srclen.i,dstlen.i,t.b,l.l
srclen=MemorySize(*src)
dstlen=MemorySize(*dst)
If (srclen=dstlen) And (srclen>0)
If srclen>=SizeOf(Long)
Repeat
!MOV ecx,dword [p.p_src]
!MOV edx,dword [p.p_dst]
!MOV eax,dword [ecx]
!XCHG dword [edx],eax
!XCHG dword [ecx],eax
srclen-SizeOf(Long)
*src+SizeOf(Long)
*dst+SizeOf(Long)
Until srclen<SizeOf(Long)
EndIf
If srclen>0
Repeat
t=PeekB(*src)
PokeB(*src,PeekB(*dst))
PokeB(*dst,t)
srclen-SizeOf(Byte)
*src+SizeOf(Byte)
*dst+SizeOf(Byte)
Until srclen<SizeOf(Byte)
EndIf
result=#True
EndIf
ProcedureReturn result
EndProcedure
Procedure.i MemorySwapASM(*src,*dst) ;Swap PB allocated memory, size must be the same.
Protected result.i=#False,srclen.i,dstlen.i,t.b
srclen=MemorySize(*src)
dstlen=MemorySize(*dst)
If (srclen=dstlen) And (srclen>0)
If srclen>=SizeOf(Long)
Repeat
!MOV ecx,dword [p.p_src]
!MOV eax,dword [ecx]
!MOV edx,dword [p.p_dst]
!XOR eax,dword [edx]
!XOR dword [edx],eax
!XOR eax,dword [edx]
!MOV dword [ecx],eax
srclen-SizeOf(Long)
*src+SizeOf(Long)
*dst+SizeOf(Long)
Until srclen<SizeOf(Long)
EndIf
If srclen>0
Repeat
t=PeekB(*src)
PokeB(*src,PeekB(*dst))
PokeB(*dst,t)
srclen-SizeOf(Byte)
*src+SizeOf(Byte)
*dst+SizeOf(Byte)
Until srclen<SizeOf(Byte)
EndIf
result=#True
EndIf
ProcedureReturn result
EndProcedure
Procedure.i MemorySwap2(*src.Quad,*dst.Quad) ;Swap PB allocated memory, size must be the same.
Protected result.i=#False,srclen.i,dstlen.i,t.b
srclen=MemorySize(*src)
dstlen=MemorySize(*dst)
If (srclen=dstlen) And (srclen>0)
If srclen>=SizeOf(Quad)
Repeat
Swap *src\q,*dst\q
srclen-SizeOf(Quad)
*src+SizeOf(Quad)
*dst+SizeOf(Quad)
Until srclen<SizeOf(Quad)
EndIf
If srclen>0
Repeat
t=PeekB(*src)
PokeB(*src,PeekB(*dst))
PokeB(*dst,t)
srclen-SizeOf(Byte)
*src+SizeOf(Byte)
*dst+SizeOf(Byte)
Until srclen<SizeOf(Byte)
EndIf
result=#True
EndIf
ProcedureReturn result
EndProcedure
Procedure.i MemorySwap(*src.Long,*dst.Long) ;Swap PB allocated memory, size must be the same.
Protected result.i=#False,srclen.i,dstlen.i,t.b
srclen=MemorySize(*src)
dstlen=MemorySize(*dst)
If (srclen=dstlen) And (srclen>0)
If srclen>=SizeOf(Long)
Repeat
Swap *src\l,*dst\l
srclen-SizeOf(Long)
*src+SizeOf(Long)
*dst+SizeOf(Long)
Until srclen<SizeOf(Long)
EndIf
If srclen>0
Repeat
t=PeekB(*src)
PokeB(*src,PeekB(*dst))
PokeB(*dst,t)
srclen-SizeOf(Byte)
*src+SizeOf(Byte)
*dst+SizeOf(Byte)
Until srclen<SizeOf(Byte)
EndIf
result=#True
EndIf
ProcedureReturn result
EndProcedure
CompilerIf #PB_Compiler_Debugger
;Example
Define size.i,*source,*destination,*pos.Byte,text$
size=15
*source=AllocateMemory(size)
*destination=AllocateMemory(size)
FillMemory(*source,size,$AB,#PB_Byte)
FillMemory(*destination,size,$CD,#PB_Byte)
text$=""
For *pos=*source To *source+(size-1)
text$+RSet(Hex(*pos\b,#PB_Byte),2,"0")
Next
Debug "Src before: "+text$
text$=""
For *pos=*destination To *destination+(size-1)
text$+RSet(Hex(*pos\b,#PB_Byte),2,"0")
Next
Debug "Dst before: "+text$
Debug ""
If MemorySwapASM(*source,*destination)
text$=""
For *pos=*source To *source+(size-1)
text$+RSet(Hex(*pos\b,#PB_Byte),2,"0")
Next
Debug "Src after: "+text$
text$=""
For *pos=*destination To *destination+(size-1)
text$+RSet(Hex(*pos\b,#PB_Byte),2,"0")
Next
Debug "Dst after: "+text$
Else
Debug "Size not equal!"
EndIf
FreeMemory(*source)
FreeMemory(*destination)
CompilerElse
;Speed test, Compile without debugger to run.
Define size.i,*source,*destination
Define t.l,t1.l,t2.l,t3.l,t4.l,memerror.i=#False
timeBeginPeriod_(1)
size=1024*1024*100 ;set this as high as you are able to.
*source=AllocateMemory(size)
*destination=AllocateMemory(size)
If *source=#Null
memerror=#True
EndIf
If *destination=#Null
memerror=#True
EndIf
If Not memerror
FillMemory(*source,size,$AB,#PB_Byte)
FillMemory(*destination,size,$CD,#PB_Byte)
t1=timeGetTime_()
MemorySwap(*source,*destination)
t=timeGetTime_()
t1=t-t1
t2=timeGetTime_()
MemorySwapASM(*source,*destination)
t=timeGetTime_()
t2=t-t2
t3=timeGetTime_()
MemorySwapASM2(*source,*destination)
t=timeGetTime_()
t3=t-t3
t4=timeGetTime_()
MemorySwap2(*source,*destination)
t=timeGetTime_()
t4=t-t4
MessageRequester("Result","Processor=x"+Str(#Processor)+", T1="+Str(t1)+"ms, "+"T2="+Str(t2)+"ms, "+"T3="+Str(t3)+"ms, "+"T4="+Str(t4)+"ms.")
Else
MessageRequester("Result","Not enough memory for allocations!")
EndIf
If *source
FreeMemory(*source)
EndIf
If *destination
FreeMemory(*destination)
EndIf
timeEndPeriod_(1)
;End
CompilerEndIf
End
And here is one that will work with any memory and length, and probably what the original poster really wanted, speed should be the same as MemorySwap2() above:
Code: Select all
Procedure.i MemorySwap(*src.Quad,*dst.Quad,length.i)
Protected result.i=#False,t.b
If length>0
If length>=SizeOf(Quad)
Repeat
Swap *src\q,*dst\q
length-SizeOf(Quad)
*src+SizeOf(Quad)
*dst+SizeOf(Quad)
Until length<SizeOf(Quad)
EndIf
If length>0
Repeat
t=PeekB(*src)
PokeB(*src,PeekB(*dst))
PokeB(*dst,t)
length-SizeOf(Byte)
*src+SizeOf(Byte)
*dst+SizeOf(Byte)
Until length<SizeOf(Byte)
EndIf
result=#True
EndIf
ProcedureReturn result
EndProcedure
EDIT: Fixed a bug with MemorySwap(*src.Quad,*dst.Quad,length.i) the byte copy part didn't reduce length count, oops.