I was curious which was faster to move values up in a group of arrays,
a loop or copymemory.
For some reason program blowing up on last CopyMemory call.
Any help/suggestions appreciated?
Thanks
David
Printable View
I was curious which was faster to move values up in a group of arrays,
a loop or copymemory.
For some reason program blowing up on last CopyMemory call.
Any help/suggestions appreciated?
Thanks
David
You still have to loop to copy memory, don't you? Or are you going to read a large block at once to copy 4K of info? That may be the problem.
Copymemory is much faster.
It is more difficult to move the array up than down
VB Code:
'move array up '1st to temp array CopyMemory temp(LBound(temp)), arr(LBound(arr)), (UBound(arr) - 1) * Len(arr(UBound(arr))) 'back to our array CopyMemory arr(LBound(arr) + 1), temp(LBound(temp)), (UBound(arr) - 1) * Len(arr(UBound(arr))) 'move array down CopyMemory arr(LBound(arr)), arr(LBound(arr) + 1), (UBound(arr) - 1) * Len(arr(UBound(arr)))
moeur
Thanks for taking your time to respond and pointing out my error.
I'm still not convinced CopyMemory is faster. From my testing so far,
like most things it depends. If you have multiple arrays then the loop
may be faster than multiple calls to CopyMemory.
From the tests I've run so far, the Loop = CopyMemory till around 12000 elements (at least will 5 arrays), then CopyMemory wins.
Attached is a revised example.
David.
I'm not altogether sure what you're trying to achieve, but RtlMoveMemory a.k.a. CopyMemory is always faster than a loop.
Also, there is no need for a temporary array. RtlMoveMemory can handle overlapped blocks of memory.
VB Code:
' Move array elements up CopyMemory arr(LBound(arr)), arr(LBound(arr) + 1), (UBound(arr) - 1) * LenB(arr(UBound(arr)))
Also, your test is non-existent. I had to loop it 1000 times for a result to show ;)
Code:Loop: 1.875 seconds
CopyMemory: 0.047 seconds
I found your question interesting so I made a test project myself :)
For me, no matter what the number of elements, MoveMemory always takes the same amount of time.
CopyMemory will always be faster because it is a low level API call. Odds are, the function was made in Assembly in like a couple lines of code. And that will most certainly be way faster than VB, hands down.
Quite apart from that, it is one call no matter the number of elements to shift. The time is therefore always constant, whereas with a one-by-one element moving loop the time will scale linearly relative to the number of elements.
An API call will always be disgustingly complex compared to straight assembly though. If VB had something like a MemMove statement then it would be much faster than using RtlMoveMemory.
There is, well sort of. It's called =
:bigyello:
Only thing is that it can't copy entire array's instantly.
Thanks for all the comments.
The reason for the post was that I have multiple arrays whose
elements need to be moved up/down to allow insertion.
The question than became is it faster to loop once, moving all elements
or make multiple calls to CopyMemory (here my assumption is the OS needs to either reallocate a block of memory and then copy OR reallocate one additional memory area and then move the pointers.) which at some point may take longer.
--------------------------------------------------------------------
I also have another interesting question -- maybe should be another post.
Question: When using a timer less than a one second interval (1000),
is it more efficient to pick a multiple of 60 (here Hertz). So 480 would be better than 500.
David
Depends what sort of timer you use. If it is a VB timer control it won't be accurate to less than a second anyway. If it is an API timer it should be accurate to a bit less than a tenth (JR's the expert here).
Thanks for response Penagate.
Had forgot about the difference between the two.
Using a supplied ActiveX to link to a server.
Had previously tried using an API timer in their control but did wierd things. For some reason the VB timer worked as it should so went with it.
Now going back over my code to try to speed things up a little. Saw yesterday I was running behind. Previously got info from the server about once every 10 seconds. Now getting info about once a second. So thought I would see if copying arrays would help as well as decreasing my buffer timer to 480 from 1000.
David
Actually I know a method that's 1 ms accurate. ;)
If you copy only a few bytes of data, then a loop is certainly faster. Calling API isn't all that fast, unfortunatenaly, so when you have only a few bytes to move, there is no point calling an API for that. If you need to move, say, over 100 bytes, then API is worthwhile (I haven't done a comparison test, so I don't know the critical point).
As always thanks for input.
Jacob Roman are you going to enlighten us regarding:
DavidQuote:
Actually I know a method that's 1 ms accurate.
Can we use the CopyMemory to copy a string array that is returned in Variant, please see my post here and any help will be appreciated:
http://www.vbforums.com/showthread.php?t=348703
Thanks
The best method for timing is the Performance Counters
This gives a resolution of about 300 nanoseconds if supported.
Code below
I used it to run speed tests on copymemory vs loop method
copymemory is always faster than loop method even with only 25 elements in the array
I was suprised to learn (thanks pengate) that copymemory will handle block copies in both directions. This means that the routine has to calculate whether the destination is going to be over written and if so copy it to a buffer.
This suspicion is born out by the fact that the below shiftUp routine takes more than 3 times as long as the shift down routine. The loop methos takes 10 times as long as the ShiftUp routine.
I posted this timing stuff in the codebank FAQ thread before they made us stop posting there:(
VB Code:
Option Explicit Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) Private Declare Function QueryPerformanceCounter Lib "Kernel32" _ (X As Currency) As Boolean Private Declare Function QueryPerformanceFrequency Lib "Kernel32" _ (X As Currency) As Boolean Private Type TimerData StartCount As Currency StopCount As Currency Overhead As Currency Frequency As Currency End Type Const ArrayMax = 2500 'returns the number of seconds elapsed between StopCount and StartCount 'Assumes that InitPerformanceTimer has been called Private Function ElapsedTime(mydata As TimerData) As Single With mydata ElapsedTime = (.StopCount - .StartCount - .Overhead) / .Frequency 'seconds End With End Function 'Initializes the data structure so that the ElapsedTime function can be called 'Returns false if the High-resolution timer is not supported Private Function InitPerformanceTimer(mydata As TimerData) As Boolean Dim Ctr1 As Currency, Ctr2 As Currency With mydata If QueryPerformanceCounter(Ctr1) Then QueryPerformanceCounter Ctr2 QueryPerformanceFrequency .Frequency '1/Freq*10000 is resolution of timer in seconds Debug.Print "QueryPerformanceCounter minimum resolution: 1/" & _ .Frequency * 10000; " seconds" 'This overhead is present for each call to API .Overhead = Ctr2 - Ctr1 Debug.Print "API Overhead: "; .Overhead / .Frequency; "seconds" InitPerformanceTimer = True Else Debug.Print "High-resolution counter not supported." InitPerformanceTimer = False End If End With End Function Sub LoopMethod(myArray() As Single) Dim i As Integer 'shift up For i = UBound(myArray) To LBound(myArray) + 1 Step -1 myArray(i) = myArray(i - 1) Next i End Sub Sub ShiftDown(myArray() As Single) 'shift array down one Dim Begin As Integer Begin = LBound(myArray) CopyMemory myArray(Begin), myArray(Begin + 1), (UBound(myArray) - Begin) * Len(myArray(Begin)) End Sub Sub ShiftUp(myArray() As Single) 'shift array up one Dim Begin As Integer Begin = LBound(myArray) CopyMemory myArray(Begin + 1), myArray(Begin), (UBound(myArray) - Begin) * Len(myArray(Begin)) End Sub Private Sub Command1_Click() Dim i As Integer Dim mydata As TimerData InitPerformanceTimer mydata Dim sngArray(0 To ArrayMax) As Single For i = 0 To ArrayMax sngArray(i) = i / 10 Next i QueryPerformanceCounter mydata.StartCount For i = 1 To 100 ShiftUp sngArray Next i QueryPerformanceCounter mydata.StopCount Debug.Print "ShiftUp Elapsed time is " & CStr(ElapsedTime(mydata) * 1000) & " milliseconds." QueryPerformanceCounter mydata.StartCount For i = 1 To 100 ShiftDown sngArray Next i QueryPerformanceCounter mydata.StopCount Debug.Print "ShiftDown Elapsed time is " & CStr(ElapsedTime(mydata) * 1000) & " milliseconds." QueryPerformanceCounter mydata.StartCount For i = 1 To 100 LoopMethod sngArray Next i QueryPerformanceCounter mydata.StopCount Debug.Print "Loopmethod(Up) Elapsed time is " & CStr(ElapsedTime(mydata) * 1000) & " milliseconds." End Sub
Thanks moeur for your input as well as everyone else.
I hope we all learned something today -- I did!
I have one request: place the comparison code for each test in their own command button. I've found out that for some reason the results seem to get distorted when you do multiple benchmarks within one procedure.
I tested the critical point: with 15 elements VB6 is faster. With 16 elements using API is faster. Though, this is when we are both reading and writing to an array. I don't (yet) know the critical point for just reading the array (of which I personally need information of).
Edit No wait a second here... *sigh* we were also testing the speed of UBound and LBound here! Retest...
Edit #2 With just the required code and without anything needless, then with 12 elements VB6 is faster, with 13 elements API.
You're right, I just ran it from the IDEQuote:
it seems you didn't compile the program using all advanced optimizations! This is critical! VB6 speeds up a huge amount when you compile your program
Putting all methods into their own button and compiling for speed, I see that the loop method beats the copymemory when shifting up (only) with 20 elements.
Loop method beats copymemory when shifting down with 9 elements in the array.
with 2500 elements in the array the loop method is only now about 2 times slower than copymemory shift up, and 7.5 times slower than copymemory shift down.
Ok, I've done some more testing (and correcting my thinking, I'm tired at this time of day being 1 AM already). I've found out, that VB6 keeps being faster when shifting items up. VB6 doesn't even lose badly when copying million items the other way. Here is my code:
VB Code:
Option Explicit Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" _ (Destination As Any, Source As Any, ByVal Length As Long) Private Declare Function QueryPerformanceCounter Lib "Kernel32" (X As Currency) As Boolean Private Declare Function QueryPerformanceFrequency Lib "Kernel32" (X As Currency) As Boolean Private Type TimerData StartCount As Currency StopCount As Currency Overhead As Currency Frequency As Currency End Type Const ARRAYMAX As Long = 1000000 Const ITERATIONS As Long = 10 Dim MyData As TimerData Dim sngArray(0 To ARRAYMAX) As Long 'returns the number of seconds elapsed between StopCount and StartCount 'Assumes that InitPerformanceTimer has been called Private Function ElapsedTime(MyData As TimerData) As Single With MyData ElapsedTime = (.StopCount - .StartCount - .Overhead) / .Frequency 'seconds End With End Function 'Initializes the data structure so that the ElapsedTime function can be called 'Returns false if the High-resolution timer is not supported Private Function InitPerformanceTimer(MyData As TimerData) As Boolean Dim Ctr1 As Currency, Ctr2 As Currency With MyData If QueryPerformanceCounter(Ctr1) Then QueryPerformanceCounter Ctr2 QueryPerformanceFrequency .Frequency '1/Freq*10000 is resolution of timer in seconds Debug.Print "QueryPerformanceCounter minimum resolution: 1/" & _ .Frequency * 10000; " seconds" 'This overhead is present for each call to API .Overhead = Ctr2 - Ctr1 Debug.Print "API Overhead: "; .Overhead / .Frequency; "seconds" InitPerformanceTimer = True Else Debug.Print "High-resolution counter not supported." InitPerformanceTimer = False End If End With End Function Private Sub Command1_Click() Dim I As Long Dim Begin As Long, DataLength As Long For I = 0 To ARRAYMAX sngArray(I) = I Next I Begin = LBound(sngArray) DataLength = (UBound(sngArray) - Begin) * Len(sngArray(Begin)) QueryPerformanceCounter MyData.StartCount For I = 1 To ITERATIONS CopyMemory sngArray(Begin + 1), sngArray(Begin), DataLength Next I QueryPerformanceCounter MyData.StopCount Label1.Caption = CStr(ElapsedTime(MyData) * 1000) & " milliseconds." End Sub Private Sub Command2_Click() Dim I As Long Dim Begin As Long, DataLength As Long For I = 0 To ARRAYMAX sngArray(I) = I Next I Begin = LBound(sngArray) DataLength = (UBound(sngArray) - Begin) * Len(sngArray(Begin)) QueryPerformanceCounter MyData.StartCount For I = 1 To ITERATIONS CopyMemory sngArray(Begin), sngArray(Begin + 1), DataLength Next I QueryPerformanceCounter MyData.StopCount Label2.Caption = CStr(ElapsedTime(MyData) * 1000) & " milliseconds." End Sub Private Sub Command3_Click() Dim I As Long, J As Long Dim Begin As Long, Ending As Long For I = 0 To ARRAYMAX sngArray(I) = I Next I Begin = LBound(sngArray) + 1 Ending = UBound(sngArray) QueryPerformanceCounter MyData.StartCount For I = 1 To ITERATIONS For J = Begin To Ending sngArray(J - 1) = sngArray(J) Next J Next I QueryPerformanceCounter MyData.StopCount Label3.Caption = CStr(ElapsedTime(MyData) * 1000) & " milliseconds." End Sub Private Sub Command4_Click() Dim I As Long, J As Long Dim Begin As Long, Ending As Long For I = 0 To ARRAYMAX sngArray(I) = I Next I Begin = LBound(sngArray) + 1 Ending = UBound(sngArray) QueryPerformanceCounter MyData.StartCount For I = 1 To ITERATIONS For J = Ending To Begin Step -1 sngArray(J) = sngArray(J - 1) Next J Next I QueryPerformanceCounter MyData.StopCount Label4.Caption = CStr(ElapsedTime(MyData) * 1000) & " milliseconds." End Sub Private Sub Form_Load() Dim I As Integer InitPerformanceTimer MyData End Sub
So I wouldn't call VB6 array handling very slow :) So my message here would be: "think about what you are testing!"