LarsJ

Using C# and VB Code in AutoIt through .NET Framework

8 posts in this topic




#2 ·  Posted (edited)

Real C# and VB examples

One of the great advantages of AutoIt is that the code development process is easy and fast. One of the (few) disadvantages is that the code execution speed is limited by the fact that AutoIt is an interpreted language. Especially calculations in loops eg. calculations on arrays with many elements can be slow compared to the speed of the same calculations in compiled code.

Another problem related to arrays is that AutoIt arrays cannot easily be accessed from compiled languages.

Nearly a year ago, a technique was introduced in Accessing AutoIt Variables to access AutoIt arrays from compiled code. But this technique is complicated and external development tools are needed to generate the compiled code.

The possibility to use C# and VB code in AutoIt through .NET Framework makes it easy to execute compiled code in an AutoIt script, and it makes it easy to pass arrays back and forth between the AutoIt code and the compiled code. And everything (write, compile, load and execute the code and even create an assembly dll-file) can be done through AutoIt.

Although it's different languages, the difference between pure calculations and loops is often not so great. If the C# and VB code is restricted to a single function or a single central loop, that's crucial for the execution speed, it should not be too hard to convert AutoIt code to C# or VB code.

With the possibility to use C# and VB code in AutoIt through .NET Framework, processing loops and arrays in compiled code has never been easier.

C# and VB are very popular programming languages. There are literally millions of examples on the internet. This means that you almost never have to start completely from scratch. You can almost always find an example to copy.

Four new examples
In first post, an example of calculating prime numbers has been examined. The C#/VB code is 100 times faster than the AutoIt code.

This post is a review of four new examples. The examples are about generating a 2D array of random data, sorting the array by one or more columns through an index, converting the 2D array to a 1D array in CSV format, and finally saving the 1D array as a CSV file.

The purpose of the examples is to show how to convert AutoIt code to compiled code in different but common areas. The examples also shows which types of code can be optimized and which types of code cannot be much optimized.

The compiled code in these examples is VB code. There is no C# code. For each example there is an implementation in pure AutoIt code, and an implementation in AutoIt/VB code. Files related to optimized AutoIt/VB code has "Opt" in the file name.

A little bit of error checking is done in each of the examples. At least checking function parameters. All error checking is of course done in AutoIt code. There seems not to be much point in optimizing error checking with compiled code.

The examples are added to the zip-file in bottom of first post. They are stored in Examples\2) Real C# and VB examples\. All four examples are structured in the same way. There is a main folder and two subfolders: Examples\ and Includes\. The subfolders contains a handful of files.

The code in the second example is a continuation of the code in the first example. The code in the third example is a continuation of the code in the first and second example. The code in the last example is a continuation of the code in the previous examples.

If you want to try all the examples at once you can go directly to the last example. If you run the example with pure AutoIt code and the example with optimized AutoIt/VB code, you can easily get an impression of the speed difference.

Because arrays and CSV-files are large arrays and files, they are shown in virtual listviews with _ArrayDisplayEx() and CSVfileDisplay(). These functions are stored in Display\ folder in the zip-file.

Below is a review of the four examples with focus on the second example about sorting.

Generate random data
The code in this example creates a 2D array of random data where the columns can contain 5 different data types: strings, integers, floats (doubles), dates and times. Dates and times are integers on the formats yyyymmdd and hhmmss, and are created as correct dates and times. See Includes\Rand2DArray.txt for documentation.

The optimized AutoIt/VB code is about 10 times faster than the pure AutoIt code.

Index based sorting
A 2D array can be sorted by one or more columns. Sorting by more columns is relevant if the columns contains duplicates. A column can be sorted as either strings or numbers (integers or floats) in ascending or descending order.  See Includes\Sort2DArray.txt for documentation.

That it's an index based sorting means that the array itself is not sorted but that an index is created that contains the array row indices in an order that matches the sorting order. This is usually a good sorting technique for arrays with many rows and columns where more than one column is included in the sorting. And it makes it possible to sort the same array in several ways by creating multiple sorting indexes.

This is the AutoIt sorting code:

; Index based sorting of a 2D array by one or more columns. Returns the
; sorting index as a DllStruct (Sort2DArray) or an array (Sort2DArrayOpt).
Func Sort2DArray( _
  $aArray, _  ; The 2D array to be sorted by index
  $aCompare ) ; Info about columns used in sorting, see 1) in docu

  ; Check parameters
  ; ...

  Local $iRows = UBound( $aArray )
  Local $iCmps = UBound( $aCompare )
  Local $tIndex = DllStructCreate( "uint[" & $iRows & "]" )
  Local $pIndex = DllStructGetPtr( $tIndex )
  Static $hDll = DllOpen( "kernel32.dll" )

  ; Sorting by multiple columns
  Local $lo, $hi, $mi, $r, $j
  For $i = 1 To $iRows - 1
    $lo = 0
    $hi = $i - 1
    Do
      $r = 0 ; Compare result (-1,0,1)
      $j = 0 ; Index in $aCompare array
      $mi = Int( ( $lo + $hi ) / 2 )
      While $r = 0 And $j < $iCmps
        $r = ( $aCompare[$j][1] ? StringCompare( $aArray[$i][$aCompare[$j][0]], $aArray[DllStructGetData($tIndex,1,$mi+1)][$aCompare[$j][0]] ) : _
               $aArray[$i][$aCompare[$j][0]] < $aArray[DllStructGetData($tIndex,1,$mi+1)][$aCompare[$j][0]] ? -1 : _
               $aArray[$i][$aCompare[$j][0]] > $aArray[DllStructGetData($tIndex,1,$mi+1)][$aCompare[$j][0]] ? 1 : 0 ) * $aCompare[$j][2]
        $j += 1
      WEnd
      Switch $r
        Case -1
          $hi = $mi - 1
        Case  1
          $lo = $mi + 1
        Case  0
          ExitLoop
      EndSwitch
    Until $lo > $hi
    DllCall( $hDll, "none", "RtlMoveMemory", "struct*", $pIndex+($mi+1)*4, "struct*", $pIndex+$mi*4, "ulong_ptr", ($i-$mi)*4 )
    DllStructSetData( $tIndex, 1, $i, $mi+1+($lo=$mi+1) )
  Next

  Return $tIndex
EndFunc

Note that a DllStruct is used to implement the sorting index.

And this is the VB sorting code:

Public Function Sort2DArray( aObjects As Object(,), aCompare As Object(,) ) As Integer()
  Dim iRows As Integer = aObjects.GetUpperBound(1) + 1
  Dim iCmps As Integer = aCompare.GetUpperBound(1) + 1
  Dim MyList As New Generic.List( Of Integer )
  MyList.Capacity = iRows
  MyList.Add(0)

  'Sorting by multiple columns
  Dim lo, hi, mi, r, j As Integer
  For i As Integer = 1 To iRows - 1
    lo = 0
    hi = i - 1
    Do
      r = 0 'Compare result (-1,0,1)
      j = 0 'Index in $aCompare array
      mi = ( lo + hi ) / 2
      While r = 0 And j < iCmps
        r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),i), aObjects(aCompare(0,j),MyList.Item(mi)) ),
            If( aObjects(aCompare(0,j),i) < aObjects(aCompare(0,j),MyList.Item(mi)), -1,
            If( aObjects(aCompare(0,j),i) > aObjects(aCompare(0,j),MyList.Item(mi)), 1, 0 ) ) ) * aCompare(2,j)
        j += 1
      End While
      Select r
        Case -1
          hi = mi - 1
        Case 1
          lo = mi + 1
        Case 0
          Exit Do
      End Select
    Loop Until lo > hi
    MyList.Insert( If(lo=mi+1,mi+1,mi), i )
  Next

  Return MyList.ToArray()
End Function

Here MyList is used to implement the sorting index. A list in VB is a 1D array where items can be inserted into the list without the need for manually (in a loop) to move subsequent items a row down to get room for the new item. This is handled internally in the implementation of the list.

In both the AutoIt and VB code, the index is created through a binary sorting and insertion algorithm.

Note the similarity between the AutoIt and the VB code. Due to the list the VB code seems to be the easiest code.

The optimized AutoIt/VB code is about 10 times faster than the pure AutoIt code.

Convert array
The 2D array is converted into a 1D array of strings in CSV format. The most remarkable of this example is that the AutoIt/VB code is only 1-2 times faster than the pure AutoIt code. There are mainly two reasons for this. Firstly, it's very simple code and that's to the advantage of the AutoIt code. Secondly, the COM conversions (discussed in a section in Accessing AutoIt Variables) plays an important role. And that's to the disadvantage of the VB code.

Save array
In the last example the 1D array of strings is saved as a CSV file. The AutoIt/VB code is a bit faster than the pure AutoIt code. The execution speed is predominantly determined by how fast a file can be written to the disk drive. This takes approximately equal time in VB and AutoIt code.

Note that the VB code does not contain a loop to save the strings. There is an internal function for storing a 1D array of strings.


Zip-file at bottom of first post is updated.

Edited by LarsJ
"Save array" section updated
2 people like this

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

UDF Version of examples

"Examples\3) UDF version of examples" in zip-file in bottom of first post contains a version of the optimized AutoIt/VB examples in the post above implemented as a single UDF. There is also a DLL-version of the UDF.

Note the initialization function at the top of Includes\ArrayFuncsOpt.au3:

Func ArrayFuncsOptInit()
  Static $oNetCode = 0, $oArrayClass = 0
  If IsObj( $oArrayClass ) Then Return $oArrayClass
  ; Compile and load VB code, create ArrayClass object
  $oNetCode = DotNet_LoadVBcode( FileRead( "..\Includes\ArrayFuncsOpt.vb" ), "System.dll" )
  $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" )
  Return $oArrayClass
EndFunc

$oNetCode is a reference to the VB code. $oArrayClass is an object that's created from the ArrayClass in the VB code. These variables are static to be able to avoid any global variables.

The initialization function is usually called at the beginning of the user script. But the initialization function is also called in each of the four functions in ArrayFuncsOpt.au3 in this way:

Static $oArrayClass = 0
If $oArrayClass = 0 Then $oArrayClass = ArrayFuncsOptInit()

If the initialization function isn't called it'll work anyway. This is very convenient for example in relation to tests.

DLL-version
In a production environment the compiled VB code should be stored in a .NET assembly DLL-file, and the AutoIt user code should be compiled as an EXE-file.

In the DLL-version the initialization function looks like this:

Func ArrayFuncsOptInit()
  Static $oNetCode = 0, $oArrayClass = 0
  If IsObj( $oArrayClass ) Then Return $oArrayClass
  ; Load ArrayFuncsOpt.dll and create ArrayClass object
  $oNetCode = DotNet_LoadAssembly( "ArrayFuncsOpt.dll" )
  $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" )
  Return $oArrayClass
EndFunc

If the .NET assembly DLL-file is stored in the same folder as the AutoIt EXE-file, the AutoIt code is always able to find and load the DLL-file.

Edited by LarsJ
Updated

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

Adv. C# and VB examples

Threading

Perform a lengthy calculation of prime numbers in a worker thread in VB code while the AutoIt script is still responsive in the main thread.

Code in "Examples\4) Adv. C# and VB examples\Threading" in zip-file in bottom of first post.

Copy code
We'll start by copying TemplateVB.au3 and TemplateVB.vb from "Examples\1) Introductory C# and VB examples\0) Code templates". The files are renamed to tst00.au3 and tst00.vb. tst00.vb:

Imports System
Class Au3Class
  Public Sub MyMethod()
    Console.WriteLine( "Hello world from VB!" )
  End Sub
End Class

tst00.au3:

#include "..\..\..\Includes\DotNetAll.au3"

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst00.vb" ), "System.dll" )
  Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" )
  $oAu3Class.MyMethod()
EndFunc

#cs
Hello world from VB!
#ce

Copy tst00.au3/tst00.vb to tst01.au3/tst01.vb. Copy this VB code from Microsoft documentation to tst01.vb. tst01.vb must look like this:

'https://msdn.microsoft.com/en-us/library/system.threading.thread(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#Starting

Imports System
Imports System.Threading
Imports System.Diagnostics

Class Au3Class
  Public Sub MyMethod()
    Dim th As New Thread(AddressOf ExecuteInForeground)
    th.Start()
    Thread.Sleep(1000)
    Console.WriteLine("Main thread ({0}) exiting...", Thread.CurrentThread.ManagedThreadId) 
  End Sub

  Private Sub ExecuteInForeground()
    Dim start As DateTime = DateTime.Now
    Dim sw As Stopwatch = Stopwatch.StartNew()
    Console.WriteLine("Thread {0}: {1}, Priority {2}", 
                      Thread.CurrentThread.ManagedThreadId,
                      Thread.CurrentThread.ThreadState,
                      Thread.CurrentThread.Priority)
    Do 
      Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds", 
                        Thread.CurrentThread.ManagedThreadId,
                        sw.ElapsedMilliseconds / 1000)
      Thread.Sleep(500)
    Loop While sw.ElapsedMilliseconds <= 5000
    sw.Stop() 
  End Sub
End Class

tst01.au3:

#include "..\..\..\Includes\DotNetAll.au3"

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst01.vb" ), "System.dll" )
  Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" )
  $oAu3Class.MyMethod()
EndFunc

#cs
Thread 3: Running, Priority Normal
Thread 3: Elapsed 0,00 seconds
Thread 3: Elapsed 0,50 seconds
Main thread (1) exiting...
Thread 3: Elapsed 1,02 seconds
#ce

When we run the code in tst01.au3 there seems to be something wrong. Some of the console output is missing.

The problem is that there is no code in tst01.au3 to keep the script running. tst01.au3 exits as soon as $oAu3Class.MyMethod() has been executed. And when tst01.au3 exits, the .NET Framework host exits, the VB code exits and the worker thread that was created within the VB code exits.

Lets add a Sleep(5000) to keep the script alive: tst02.au3:

#include "..\..\..\Includes\DotNetAll.au3"

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst01.vb" ), "System.dll" )
  Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" )
  $oAu3Class.MyMethod()
  Sleep(5000)
EndFunc

#cs
Thread 3: Running, Priority Normal
Thread 3: Elapsed 0,00 seconds
Thread 3: Elapsed 0,51 seconds
Main thread (1) exiting...
Thread 3: Elapsed 1,03 seconds
Thread 3: Elapsed 1,54 seconds
Thread 3: Elapsed 2,06 seconds
Thread 3: Elapsed 2,57 seconds
Thread 3: Elapsed 3,09 seconds
Thread 3: Elapsed 3,60 seconds
Thread 3: Elapsed 4,12 seconds
Thread 3: Elapsed 4,63 seconds
#ce

Now the output is similar to the output of the Microsoft example.

Add a ConsoleWrite before and after the Sleep. tst03.au3:

#include "..\..\..\Includes\DotNetAll.au3"

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst01.vb" ), "System.dll" )
  Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" )
  $oAu3Class.MyMethod()
  ConsoleWrite( "Sleep starts" & @CRLF )
  Sleep(5000)
  ConsoleWrite( "Sleep stops" & @CRLF )
EndFunc

#cs
Thread 3: Running, Priority Normal
Thread 3: Elapsed 0,00 seconds
Thread 3: Elapsed 0,50 seconds
Main thread (1) exiting...
Sleep starts
Thread 3: Elapsed 1,01 seconds
Thread 3: Elapsed 1,53 seconds
Thread 3: Elapsed 2,04 seconds
Thread 3: Elapsed 2,56 seconds
Thread 3: Elapsed 3,07 seconds
Thread 3: Elapsed 3,59 seconds
Thread 3: Elapsed 4,10 seconds
Thread 3: Elapsed 4,62 seconds
Sleep stops
#ce

The VB code in the worker thread is clearly running while the AutoIt code in the main thread is sleeping.

Use counter
Add a counter to the VB code. tst04.vb:

Imports System
Imports System.Threading
Imports System.Diagnostics

Class Au3Class
  Dim iCounter As Integer = 0

  Public Function GetCounter() As Integer
    Return iCounter
  End Function

  Public Sub MyMethod()
    Dim th As New Thread(AddressOf ExecuteInForeground)
    th.Start()
    Thread.Sleep(1000)
    Console.WriteLine("Main thread ({0}) exiting...", Thread.CurrentThread.ManagedThreadId) 
  End Sub

  Private Sub ExecuteInForeground()
    Dim start As DateTime = DateTime.Now
    Dim sw As Stopwatch = Stopwatch.StartNew()
    Console.WriteLine("Thread {0}: {1}, Priority {2}", 
                      Thread.CurrentThread.ManagedThreadId,
                      Thread.CurrentThread.ThreadState,
                      Thread.CurrentThread.Priority)
    Do 
      Console.WriteLine("Thread {0}: Elapsed {1:N2} seconds", 
                        Thread.CurrentThread.ManagedThreadId,
                        sw.ElapsedMilliseconds / 1000)
      iCounter += 1
      Thread.Sleep(500)
    Loop While sw.ElapsedMilliseconds <= 5000
    sw.Stop() 
  End Sub
End Class

Replace the Sleep with a While loop in the AutoIt code. tst04.au3:

#include "..\..\..\Includes\DotNetAll.au3"

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst04.vb" ), "System.dll" )
  Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" )
  $oAu3Class.MyMethod()
  Local $iCounter = 0
  While $iCounter < 10
    $iCounter = $oAu3Class.GetCounter()
    ConsoleWrite( "$iCounter = " & $iCounter & @CRLF )
    Sleep( 250 )
  WEnd
EndFunc

#cs
Thread 3: Running, Priority Normal
Thread 3: Elapsed 0,00 seconds
Thread 3: Elapsed 0,51 seconds
Main thread (1) exiting...
$iCounter = 2
Thread 3: Elapsed 1,02 seconds
$iCounter = 3
$iCounter = 3
Thread 3: Elapsed 1,54 seconds
$iCounter = 4
$iCounter = 4
Thread 3: Elapsed 2,05 seconds
$iCounter = 5
$iCounter = 5
Thread 3: Elapsed 2,57 seconds
$iCounter = 6
$iCounter = 6
Thread 3: Elapsed 3,08 seconds
$iCounter = 7
$iCounter = 7
Thread 3: Elapsed 3,60 seconds
$iCounter = 8
Thread 3: Elapsed 4,11 seconds
$iCounter = 9
$iCounter = 9
Thread 3: Elapsed 4,63 seconds
$iCounter = 10
#ce

Prime numbers
At this point we can insert the VB code and AutoIt code to calculate prime numbers. tst05.vb:

Imports System
Imports System.Threading

Class Au3Class
  Dim aPrimes(1) As Integer
  Dim iCntPrimes As Integer = 0

  Public Function GetNumberOfPrimes() As Integer
    Return iCntPrimes
  End Function

  Public Function GetArrayOfPrimes() As Integer()
    Return aPrimes
  End Function

  Public Sub CalcPrimes( nPrimes As Integer )
    Dim th As New Thread( AddressOf CalcPrimesSub )
    th.Start( nPrimes )
  End Sub

  Private Sub CalcPrimesSub( oPrimes As Object )
    Dim nPrimes As Integer = CInt( oPrimes ) : ReDim aPrimes(nPrimes-1)
    Dim iPrime As Integer = 2, iPrimes As Integer = 0, i As Integer
    If nPrimes <= 100 Then Console.WriteLine( iPrime )

    'Store first prime
    aPrimes(iPrimes) = iPrime
    iPrimes += 1
    iPrime += 1

    'Loop to calculate primes
    While iPrimes < nPrimes
      For i = 0 To iPrimes - 1
        If iPrime Mod aPrimes(i) = 0 Then Exit For
      Next
      If i = iPrimes Then
        If nPrimes <= 100 Then Console.WriteLine( iPrime )
        aPrimes(iPrimes) = iPrime
        iPrimes += 1
        If iPrimes Mod 1000 = 0 Then iCntPrimes = iPrimes
      End If
      iPrime += 1
    End While
    iCntPrimes = iPrimes
  End Sub
End Class

tst05.au3:

#include "..\..\..\Includes\DotNetAll.au3"
#include "..\..\..\Display\Functions\ArrayDisplayEx\ArrayDisplayEx.au3"

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst05.vb" ), "System.dll" )
  Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" )

  Local $iPrimes = 100000
  $oAu3Class.CalcPrimes( $iPrimes )

  Local $iCntPrimes = 0
  While $iCntPrimes < $iPrimes
    $iCntPrimes = $oAu3Class.GetNumberOfPrimes()
    ConsoleWrite( "$iCntPrimes = " & $iCntPrimes & @CRLF )
    Sleep( 1000 )
  WEnd

  Local $aPrimes = $oAu3Class.GetArrayOfPrimes()
  _ArrayDisplayEx( $aPrimes )
EndFunc

#cs
$iCntPrimes = 0
$iCntPrimes = 24000
$iCntPrimes = 34000
$iCntPrimes = 42000
$iCntPrimes = 49000
$iCntPrimes = 55000
$iCntPrimes = 60000
$iCntPrimes = 65000
$iCntPrimes = 69000
$iCntPrimes = 74000
$iCntPrimes = 78000
$iCntPrimes = 81000
$iCntPrimes = 85000
$iCntPrimes = 88000
$iCntPrimes = 92000
$iCntPrimes = 95000
$iCntPrimes = 98000
$iCntPrimes = 100000
#ce

AutoIt GUI
Finally we can make a GUI. tst06.au3:

#AutoIt3Wrapper_Au3Check_Parameters=-d -w- 1 -w 2 -w 3 -w 4 -w 5 -w 6

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

#include "..\..\..\Includes\DotNetAll.au3"
#include "..\..\..\Display\Functions\ArrayDisplayEx\ArrayDisplayEx.au3"

Opt( "MustDeclareVars", 1 )

Example()

Func Example()
  Local $oNetCode = DotNet_LoadVBcode( FileRead( "tst05.vb" ), "System.dll" )
  Local $oAu3Class = DotNet_CreateObject( $oNetCode, "Au3Class" )

  Local $hGui1 = GUICreate( "Prime numbers", 300, 100, 250, 250 )
  Local $idLabel = GUICtrlCreateLabel( "Primes:", 20, 22, 35, 18 )
  Local $idCombo = GUICtrlCreateCombo( "", 60, 20, 80, 20 )
  GUICtrlSetData( $idCombo, "50000|100000|150000|200000|250000", "50000" )
  Local $idPrimes = GUICtrlCreateButton( "Calculate primes", 160, 20, 120, 20 )
  Local $idButton1 = GUICtrlCreateButton( "Button1",  20, 60, 120, 20 )
  Local $idButton2 = GUICtrlCreateButton( "Button2", 160, 60, 120, 20 )

  GUISetState()

  Local $s, $hGui2, $idProgress, $idPrgPct, $hTimer = 0, $iTimes, $nPrimes, $iPrimes

  While 1
    Switch GUIGetMsg()
      Case $idPrimes
        GUICtrlSetState( $idPrimes, $GUI_DISABLE )
        $nPrimes = Int( GUICtrlRead( $idCombo ) )
        $oAu3Class.CalcPrimes( $nPrimes )
        $s = StringRegExpReplace( $nPrimes, "(\d{1,3}(?=(\d{3})+\z)|\d{3}(?=\d))", "\1,")
        $hGui2 = GUICreate( "Calculating " & $s & " primes ...", 340, 90, 350, 400, $WS_OVERLAPPED )
        $idProgress = GUICtrlCreateProgress( 20, 20, 260, 20 )
        $idPrgPct = GUICtrlCreateLabel( "0%", 290, 23, 25, 18 )
        $hTimer = TimerInit()
        $iTimes = 1
        $iPrimes = 0
        GUISetState()
      Case $idButton1
        MsgBox( 0, "Button1", "Button1", 1, $hGui1 )
      Case $idButton2
        MsgBox( 0, "Button2", "Button2", 1, $hGui1 )
      Case $GUI_EVENT_CLOSE
        ExitLoop
      Case Else
        If $hTimer And TimerDiff( $hTimer ) > $iTimes * 1000 Then
          $iTimes += 1
          $iPrimes = $oAu3Class.GetNumberOfPrimes()
          GUICtrlSetData( $idProgress, 100*$iPrimes/$nPrimes )
          GUICtrlSetData( $idPrgPct, Int( 100*$iPrimes/$nPrimes ) & "%" )
          If $iPrimes = $nPrimes Then
            Sleep(1000)
            $hTimer = 0
            GUIDelete( $hGui2 )
            GUICtrlSetState( $idPrimes, $GUI_ENABLE )
            _ArrayDisplayEx( $oAu3Class.GetArrayOfPrimes() )
          EndIf
        EndIf
    EndSwitch
  WEnd

  GUIDelete( $hGui2 )
  GUIDelete( $hGui1 )
  #forceref $idLabel
EndFunc

Av1NyHP.png

You can click Button1 and Button2 while the prime numbers are calculated. The MsgBoxes closes automatically after one second.

Edited by LarsJ
Updated

Share this post


Link to post
Share on other sites

A few updates to the examples in post 2
"2) Real C# and VB examples\1) Index based sorting\Includes\Sort2DArrayOpt.au3"
System.Collections.dll is not needed in DotNet_LoadVBcode().

"2) Real C# and VB examples\2) Convert array\Includes\Conv2DArrayOpt.vb"
CType( aObjects(j,i), String ) replaced with CStr( aObjects(j,i) ).

"2) Real C# and VB examples\3) Save array\Includes\Save1DArrayOpt.au3"
System.IO.dll and System.Text.Encoding.dll are not needed in DotNet_LoadVBcode().

"2) Real C# and VB examples\3) Save array\Examples\ExampleOpt.au3"
In the previous version (ExampleOpt-a.au3), I had forgotten to initialize the VB code with Save1DArrayOptInit() in top of the script. This meant that the initialization instead was performed at the top of Save1DArrayOpt() function, and that the runtime for the initialization (about 100 milliseconds) was included in the runtime for Save1DArrayOpt(). That made the total runtime for Save1DArrayOpt() about 100 milliseconds too high.

I've updated ExampleOpt.au3 and included Save1DArrayOptInit() in top of the script. This makes Save1DArrayOpt() about 100 milliseconds faster. And now, the optimized AutoIt/VB code is a bit faster than the pure AutoIt code. In the old version, the pure AutoIt code was fastest.

I've updated the conclusion about the runtimes in "Save array" section in bottom of post 2.


Post 3
UDF version of examples in post 2


Post 4
Threading

2 people like this

Share this post


Link to post
Share on other sites

Prime numbers

To illustrate some of the issues associated with using C# and VB code, I've used an example for calculating prime numbers. A prime number (or a prime) is a natural number greater than 1 that has no positive divisors other than 1 and itself. Therefore, this loop can be used to determine whether a number is a prime number:

For $i = 0 To $iPrimes - 1
  If Mod( $iPrime, $aPrimes[$i] ) = 0 Then ExitLoop
Next

$iPrime is the candidate number to check, $aPrimes contains the existing prime numbers and $iPrimes is the number of existing primes. If the loop is interrupted before all existing prime numbers are checked, it's not a prime number.

There is nothing wrong with this loop to determine prime numbers. But the loop can be optimized much because it's not necessary to check all existing prime numbers. It's enough to check primes up to and including the number given by this formula: Ceiling(Sqrt($iPrime)). Then the loop can be coded in this way instead of:

$iMaxFactor = Ceiling(Sqrt($iPrime))
While $i < $iPrimes And $aPrimes[$i] <= $iMaxFactor
  If Mod( $iPrime, $aPrimes[$i] ) = 0 Then $i = $iPrimes
  $i += 1
WEnd

Again if the loop is interrupted before all prime numbers are checked, it's not a prime.

The second version of the loop is a huge performance improvement. All examples are updated with the new version of the loop.

The 5 numbers in the range 50,000 - 250,000 in tst06.au3 in post 4 (the example with the progress bar) have been replaced with 5 numbers in the range 3,000,000 - 7,000,000. And that, of course, is much more fun.

Share this post


Link to post
Share on other sites

Optimizing C# and VB code

Index based sorting

The examples in post 4 is an introduction to threading. The examples here shows how VB code can be optimized using multithreading. The examples are a continuation of the code in post 2 regarding index based sorting.

Multithreaded sorting
The idea is that if 4 threads are used to sort 100,000 rows, the first thread can sort the first 25,000 rows, the next thread can sort the next 25,000 rows, etc. It results in 4 indexes that finally can be combined into one big index.

This is the code in Sort2DArrayOptEx.vb:

Imports System
Imports System.Threading
Imports System.Collections

Class SortArrayClass
  Dim aObjects, aCompare As Object(,)
  Dim iPartRows, iRestRows, iCmps As Integer
  Dim iThreads = 4, aIndex(iThreads)(), aRows(iThreads) As Integer

  Public Function Sort2DArray( aObjPar As Object(,), aCompPar As Object(,) ) As Integer()
    aObjects = aObjPar : aCompare = aCompPar
    Dim iRows As Integer = aObjects.GetUpperBound(1) + 1
    iRestRows = iRows Mod iThreads : iPartRows = ( iRows - iRestRows ) / iThreads
    iCmps = aCompare.GetUpperBound(1) + 1

    'Multithreaded index based sorting
    Dim th0 As New Thread( AddressOf Sort2DArraySub ) : th0.Start(0)
    Dim th1 As New Thread( AddressOf Sort2DArraySub ) : th1.Start(1)
    Dim th2 As New Thread( AddressOf Sort2DArraySub ) : th2.Start(2)
    Dim th3 As New Thread( AddressOf Sort2DArraySub ) : th3.Start(3)

    'Main thread sleeps until sorting is done
    While aRows(0) = 0 Or aRows(1) = 0 Or aRows(2) = 0 Or aRows(3) = 0
      Thread.Sleep(20)
    End While

    'Combine four indexes into one
    Dim aIndexAll(iRows-1) As Integer
    Dim aIndexIdx(iThreads), iLeft, iRight, r, j As Integer
    For i As Integer = 0 To iRows - 1
      iLeft = If( aIndexIdx(0) < aRows(0), 0, If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, 3 ) ) )
      iRight = iLeft

      While iRight < iThreads
        iRight += 1
        Select iRight
          Case 1
            iRight = If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) ) )
          Case 2
            iRight = If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) )
          Case 3
            iRight = If( aIndexIdx(3) < aRows(3), 3, iThreads )
        End Select
        If iRight = iThreads Then Exit While
        r = 0 'Compare result (-1,0,1)
        j = 0 'Index in $aCompare array
        While r = 0 And j < iCmps
          r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))), aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))) ),
              If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) < aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), -1,
              If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) > aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), 1, 0 ) ) ) * aCompare(2,j)
          j += 1
        End While
        Select r
          Case 1
            iLeft = iRight
          Case 0
            If aIndex(iLeft)(aIndexIdx(iLeft)) > aIndex(iRight)(aIndexIdx(iRight)) Then iLeft = iRight
        End Select
      End While

      aIndexAll(i) = aIndex(iLeft)(aIndexIdx(iLeft))
      aIndexIdx(iLeft) += 1
    Next

    Return aIndexAll
  End Function

  'Multithreaded index based sorting
  Private Sub Sort2DArraySub( oThread As Object )
    Dim iThread As Integer = CInt( oThread )
    Dim iFirstRow As Integer = If( iThread < iRestRows, iPartRows * iThread + iThread, iPartRows * iThread + iRestRows )
    Dim iLastRow As Integer = iFirstRow + If( iThread < iRestRows, iPartRows, iPartRows - 1 )
    Dim iRows As Integer = iLastRow - iFirstRow + 1

    Dim MyList As New Generic.List( Of Integer )
    MyList.Capacity = iRows
    MyList.Add(iFirstRow)

    'Sorting by multiple columns
    Dim lo, hi, mi, r, j As Integer
    For i As Integer = iFirstRow + 1 To iLastRow
      lo = iFirstRow
      hi = i - 1
      Do
        r = 0 'Compare result (-1,0,1)
        j = 0 'Index in $aCompare array
        mi = ( lo + hi ) / 2
        While r = 0 And j < iCmps
          r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),i), aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)) ),
              If( aObjects(aCompare(0,j),i) < aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), -1,
              If( aObjects(aCompare(0,j),i) > aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), 1, 0 ) ) ) * aCompare(2,j)
          j += 1
        End While
        Select r
          Case -1
            hi = mi - 1
          Case 1
            lo = mi + 1
          Case 0
            Exit Do
        End Select
      Loop Until lo > hi
      MyList.Insert( If(lo=mi+1,mi+1,mi)-iFirstRow, i )
    Next

    aIndex(iThread) = MyList.ToArray()
    aRows(iThread) = iRows
  End Sub
End Class

In Runtimes.au3 the execution times for sorting arrays with different number of rows are measuered for pure AutoIt code, for optimized VB code and for multithreaded VB code. This is the results on my PC:

Code executed as 32 bit code           Code executed as 64 bit code
============================           ============================

$iRows = 100, 6 columns                $iRows = 100, 6 columns                
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2          
-----------------------------------    -----------------------------------    
Pure AutoIt code:            4.3340    Pure AutoIt code:            3.7655    
Optimized VB code:           1.2553    Optimized VB code:           1.4281    
Multithreaded VB code:      19.0067    Multithreaded VB code:      33.5323    

$iRows = 500, 6 columns                $iRows = 500, 6 columns                
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2          
-----------------------------------    -----------------------------------    
Pure AutoIt code:           27.5916    Pure AutoIt code:           25.5753    
Optimized VB code:           2.6330    Optimized VB code:           2.8050    
Multithreaded VB code:      31.2496    Multithreaded VB code:      36.4108    

$iRows = 1000, 6 columns               $iRows = 1000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:          106.4464    Pure AutoIt code:           90.3135
Optimized VB code:          35.7356    Optimized VB code:          27.4234
Multithreaded VB code:      64.3406    Multithreaded VB code:      57.4474

$iRows = 2000, 6 columns               $iRows = 2000, 6 columns               
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2          
-----------------------------------    -----------------------------------    
Pure AutoIt code:          142.1940    Pure AutoIt code:          134.2208    
Optimized VB code:          11.6813    Optimized VB code:          10.9643    
Multithreaded VB code:      30.2622    Multithreaded VB code:      33.4628    

$iRows = 5000, 6 columns               $iRows = 5000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:          401.1128    Pure AutoIt code:          365.6661
Optimized VB code:          38.3885    Optimized VB code:          31.2551
Multithreaded VB code:      79.6997    Multithreaded VB code:      89.7060

$iRows = 10000, 6 columns              $iRows = 10000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:          826.2889    Pure AutoIt code:          691.7514
Optimized VB code:          70.5041    Optimized VB code:          65.1964
Multithreaded VB code:      89.2076    Multithreaded VB code:      60.4300

$iRows = 20000, 6 columns              $iRows = 20000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:         1816.3940    Pure AutoIt code:         1517.2513
Optimized VB code:         183.4977    Optimized VB code:         151.7480
Multithreaded VB code:     150.1049    Multithreaded VB code:     135.3445

$iRows = 50000, 6 columns              $iRows = 50000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:         5386.5884    Pure AutoIt code:         4239.2327
Optimized VB code:         602.9203    Optimized VB code:         471.3676
Multithreaded VB code:     373.1342    Multithreaded VB code:     371.5393

$iRows = 100000, 6 columns             $iRows = 100000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:        12798.0606    Pure AutoIt code:         9489.1929
Optimized VB code:        1590.7280    Optimized VB code:        1273.4829
Multithreaded VB code:     800.1118    Multithreaded VB code:     684.8815

$iRows = 250000, 6 columns             $iRows = 250000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:        44781.3723    Pure AutoIt code:        27407.9705
Optimized VB code:        7217.6899    Optimized VB code:        5222.3106
Multithreaded VB code:    2330.1729    Multithreaded VB code:    1914.2506

$iRows = 500000, 6 columns             $iRows = 500000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:       128479.2462    Pure AutoIt code:        69438.5295
Optimized VB code:       25016.8543    Optimized VB code:       16692.2426
Multithreaded VB code:    5949.0161    Multithreaded VB code:    4565.7862

$iRows = 750000, 6 columns             $iRows = 750000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:       250176.5552    Pure AutoIt code:       107785.2414
Optimized VB code:       54193.4737    Optimized VB code:       35217.0613
Multithreaded VB code:   10827.3308    Multithreaded VB code:    8186.0242

$iRows = 1000000, 6 columns            $iRows = 1000000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:       408457.7649    Pure AutoIt code:       162282.8254
Optimized VB code:       96291.4960    Optimized VB code:       62412.9602
Multithreaded VB code:   17399.9110    Multithreaded VB code:   12690.8546

$iRows = 2000000, 6 columns            $iRows = 2000000, 6 columns
Sort array by columns 0, 1, 2          Sort array by columns 0, 1, 2      
-----------------------------------    -----------------------------------
Pure AutoIt code:      1445235.5246    Pure AutoIt code:       498648.9821
Optimized VB code:      409872.8400    Optimized VB code:      282116.6518
Multithreaded VB code:   63527.4207    Multithreaded VB code:   51087.1290

Code is added to "Examples\5) Optimizing C# and VB code\1) Index based sorting\" in zip-file in bottom of first post.


Implementing a progress bar
Another advantage of performing the sorting in 4 worker threads is that the main thread can be released by the VB code and thereby making the AutoIt code responsive while the sorting is going on. This makes it possible to implement a progress bar in the AutoIt code.

The code in Sort2DArrayOptEx2.vb is changed a little bit:

Imports System
Imports System.Threading
Imports System.Collections

Class SortArrayClass
  Dim aObjects, aCompare As Object(,)
  Dim iRows, iPartRows, iRestRows, iCmps As Integer
  Dim iThreads = 4, aIndex(iThreads)(), aRows(iThreads) As Integer

  Public Sub Sort2DArray( aObjPar As Object(,), aCompPar As Object(,) )
    aObjects = aObjPar : aCompare = aCompPar : iRows = aObjects.GetUpperBound(1) + 1
    iRestRows = iRows Mod iThreads : iPartRows = ( iRows - iRestRows ) / iThreads
    iCmps = aCompare.GetUpperBound(1) + 1

    'Multithreaded index based sorting
    Dim th0 As New Thread( AddressOf Sort2DArraySub ) : th0.Start(0)
    Dim th1 As New Thread( AddressOf Sort2DArraySub ) : th1.Start(1)
    Dim th2 As New Thread( AddressOf Sort2DArraySub ) : th2.Start(2)
    Dim th3 As New Thread( AddressOf Sort2DArraySub ) : th3.Start(3)
  End Sub

  'Multithreaded index based sorting
  Private Sub Sort2DArraySub( oThread As Object )
    Dim iThread As Integer = CInt( oThread )
    Dim iFirstRow As Integer = If( iThread < iRestRows, iPartRows * iThread + iThread, iPartRows * iThread + iRestRows )
    Dim iLastRow As Integer = iFirstRow + If( iThread < iRestRows, iPartRows, iPartRows - 1 )
    Dim iSubRows As Integer = iLastRow - iFirstRow + 1

    Dim MyList As New Generic.List( Of Integer )
    MyList.Capacity = iSubRows
    MyList.Add(iFirstRow)

    'Sorting by multiple columns
    Dim lo, hi, mi, r, j As Integer
    For i As Integer = iFirstRow + 1 To iLastRow
      lo = iFirstRow
      hi = i - 1
      Do
        r = 0 'Compare result (-1,0,1)
        j = 0 'Index in $aCompare array
        mi = ( lo + hi ) / 2
        While r = 0 And j < iCmps
          r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),i), aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)) ),
              If( aObjects(aCompare(0,j),i) < aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), -1,
              If( aObjects(aCompare(0,j),i) > aObjects(aCompare(0,j),MyList.Item(mi-iFirstRow)), 1, 0 ) ) ) * aCompare(2,j)
          j += 1
        End While
        Select r
          Case -1
            hi = mi - 1
          Case 1
            lo = mi + 1
          Case 0
            Exit Do
        End Select
      Loop Until lo > hi
      MyList.Insert( If(lo=mi+1,mi+1,mi)-iFirstRow, i )
      If ( i - iFirstRow ) Mod 1000 = 0 Then aRows(iThread) = i - iFirstRow
    Next

    aIndex(iThread) = MyList.ToArray()
    aRows(iThread) = iSubRows
  End Sub

  Public Function Sort2DArrayGetCount() As Integer
    Return aRows(0) + aRows(1) + aRows(2) + aRows(3)
  End Function

  Public Function Sort2DArrayGetIndex() As Integer()
    'Combine four indexes into one
    Dim aIndexAll(iRows-1) As Integer
    Dim aIndexIdx(iThreads), iLeft, iRight, r, j As Integer
    For i As Integer = 0 To iRows - 1
      iLeft = If( aIndexIdx(0) < aRows(0), 0, If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, 3 ) ) )
      iRight = iLeft

      While iRight < iThreads
        iRight += 1
        Select iRight
          Case 1
            iRight = If( aIndexIdx(1) < aRows(1), 1, If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) ) )
          Case 2
            iRight = If( aIndexIdx(2) < aRows(2), 2, If( aIndexIdx(3) < aRows(3), 3, iThreads ) )
          Case 3
            iRight = If( aIndexIdx(3) < aRows(3), 3, iThreads )
        End Select
        If iRight = iThreads Then Exit While
        r = 0 'Compare result (-1,0,1)
        j = 0 'Index in $aCompare array
        While r = 0 And j < iCmps
          r = If( aCompare(1,j), String.Compare( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))), aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))) ),
              If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) < aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), -1,
              If( aObjects(aCompare(0,j),aIndex(iLeft)(aIndexIdx(iLeft))) > aObjects(aCompare(0,j),aIndex(iRight)(aIndexIdx(iRight))), 1, 0 ) ) ) * aCompare(2,j)
          j += 1
        End While
        Select r
          Case 1
            iLeft = iRight
          Case 0
            If aIndex(iLeft)(aIndexIdx(iLeft)) > aIndex(iRight)(aIndexIdx(iRight)) Then iLeft = iRight
        End Select
      End While

      aIndexAll(i) = aIndex(iLeft)(aIndexIdx(iLeft))
      aIndexIdx(iLeft) += 1
    Next

    Return aIndexAll
  End Function
End Class

In Progressbar.au3 the execution times are measuered for the new code. This is the results on my PC when the code is run as 64 bit code:

250,000 rows
Generate array
Array generated 782.829209100278
Start sorting ...
Sorting started 1052.33601879453
Sorting finished 3112.24138122098
Get index ...
Index received 628.809605602899

500,000 rows
Generate array
Array generated 1692.53510123118
Start sorting ...
Sorting started 1736.08362238057
Sorting finished 5300.57901794125
Get index ...
Index received 987.373807333858

750,000 rows
Generate array
Array generated 2701.3300790124
Start sorting ...
Sorting started 2846.97747093829
Sorting finished 8147.51853411496
Get index ...
Index received 1427.32770753222

1,000,000 rows
Generate array
Array generated 3671.26491871585
Start sorting ...
Sorting started 3360.98083976994
Sorting finished 11515.8238123206
Get index ...
Index received 1913.89753875819

2,000,000 rows
Generate array
Array generated 7789.33304890347
Start sorting ...
Sorting started 6773.98630303971
Sorting finished 48250.9211649065
Get index ...
Index received 3852.50335220913

Note the lines titled "Sorting started". These are the lines that measure the time it takes to execute this code:

Public Sub Sort2DArray( aObjPar As Object(,), aCompPar As Object(,) )
  aObjects = aObjPar : aCompare = aCompPar : iRows = aObjects.GetUpperBound(1) + 1
  iRestRows = iRows Mod iThreads : iPartRows = ( iRows - iRestRows ) / iThreads
  iCmps = aCompare.GetUpperBound(1) + 1

  'Multithreaded index based sorting
  Dim th0 As New Thread( AddressOf Sort2DArraySub ) : th0.Start(0)
  Dim th1 As New Thread( AddressOf Sort2DArraySub ) : th1.Start(1)
  Dim th2 As New Thread( AddressOf Sort2DArraySub ) : th2.Start(2)
  Dim th3 As New Thread( AddressOf Sort2DArraySub ) : th3.Start(3)
End Sub

Most of the time is spent by passing the array ($aArray) as a parameter from the AutoIt code to the VB method (in aObjPar).

Code is added to "Examples\5) Optimizing C# and VB code\1) Index based sorting\" in zip-file in bottom of first post.


Global array variable

Post 2 is a UDF version of four examples about generating a 2D array of random data, sorting the array by one or more columns through an index, converting the 2D array to a 1D array in CSV format, and finally saving the 1D array as a CSV file.

The code is stored in "Examples\3) UDF version of examples\" in zip-file in bottom of first post. I've added a new example, ExampleOpt2.au3, to the Examples folder, where the execution times and the total execution time for these 4 procedures are measured for an array with 250,000 rows.

This is the results:

Code executed as 32 bit code        Code executed as 64 bit code
============================        ============================

250000 rows                         250000 rows
Generate array: 995.724559926393    Generate array: 797.652719123923
Sort array:     7236.27375117504    Sort array:     5291.30258795446
Convert array:  1426.65439887983    Convert array:  1224.09933028633
Save array:     450.983570086659    Save array:     383.220670832949
Total time:     10109.6362800679    Total time:     7696.27530819767

At the bottom of the example above we saw that quite a lot of time is being used to transfer arrays between the AutoIt code and the VB code.

One way to avoid the time used to transfer arrays between AutoIt code and VB code is to store the array ($aArray/aObjects) as a global variable in the VB code. This is implemented in "Examples\5) Optimizing C# and VB code\2) Global array variable\Includes\ArrayFuncsOpt.vb".

There is also used a multithreaded version of the sorting code. And the two procedures to convert and save the array as a CSV file is combined into one procedure to avoid transfering the array of strings back and forth.

This is the time measurement for the new code:

Code executed as 32 bit code        Code executed as 64 bit code
============================        ============================

250000 rows                         250000 rows
Generate array: 696.800872597593    Generate array: 516.643380589801
Sort array:     1542.99252546023    Sort array:     1465.04082474547
Save array:     446.520844017085    Save array:     418.329314718878
Total time:     2686.31424207491    Total time:     2400.01352005415

Code in "Examples\5) Optimizing C# and VB code\2) Global array variable\" in zip-file in bottom of first post.


Delete global variables
To delete global variables used in the VB code simply delete the object. The objects are solely handled in Includes\ArrayFuncsOpt.au3 UDF (there are no objects directly in the user code). The $oArrayClass object which is an instance of the ArrayClass in the VB code is created in ArrayFuncsOptInit() in this way:

Func ArrayFuncsOptInit( $bDelObj = False )
  Static $oNetCode = 0, $oArrayClass = 0
  If $bDelObj Then
    $oArrayClass = 0
    $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" )
    Return
  EndIf
  If IsObj( $oArrayClass ) Then Return $oArrayClass
  ; Compile and load VB code, create ArrayClass object
  $oNetCode = DotNet_LoadVBcode( FileRead( "..\Includes\ArrayFuncsOpt.vb" ), "System.dll" )
  $oArrayClass = DotNet_CreateObject( $oNetCode, "ArrayClass" )
  Return $oArrayClass
EndFunc

And the object is deleted in ArrayFuncsDelObject() by calling ArrayFuncsOptInit() with the $bDelObj parameter set to True:

Func ArrayFuncsDelObject()
  ArrayFuncsOptInit( True )
EndFunc

When the old object is deleted, a new object is created. ArrayFuncsDelObject() is called in bottom of ExampleOpt2.au3.

Similar procedures are used in the example with the progress bar above to delete the old object and create a new.

3 people like this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now