6

I have to work with multiple big 2-dimensional arrays (1024 x 128 for example) and in a section of my code I need to transpose some (up to 12 of them). The procedure takes a fair amount of time and I'm trying to speed it up as much as possible. Knowing that VB.NET supports multi-threading I gave a read here and there to different sources and could come up with the following code in the main subroutine:

RunXTransposingThreads(Arr1, Arr2, Arr3, ...)

Using BackgroundWorkers as a part of my solution:

Private Sub RunXTransposingThreads(ParamArray ArraysToTranspose() As Array)
    Dim x = CInt(ArraysToTranspose.GetLength(0)) - 1
    Dim i As Integer
    For i = 0 To x
        Dim worker As New System.ComponentModel.BackgroundWorker
        AddHandler worker.DoWork, AddressOf RunOneThread
        AddHandler worker.RunWorkerCompleted, AddressOf HandleThreadCompletion
        worker.RunWorkerAsync(ArraysToTranspose(i))
    Next
End Sub

Private Sub RunOneThread(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
    Dim Transposed(,) As Single = Array.CreateInstance(GetType(Single), 0, 0) ' I need this to be like that in order to use other functions later
    Transposed = Transpose2dArray(CType(e.Argument, Single(,)))
    e.Result = Transposed
End Sub

Private Sub HandleThreadCompletion(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
    Debug.Print("Process complete")
End Sub

Function Transpose2dArray(Of Array)(ByVal inArray As Array(,)) As Array(,)

    Dim x = CInt(inArray.GetLength(1))
    Dim y = CInt(inArray.GetLength(0))
    Dim outArray(x - 1, y - 1) As Array

    For i = 0 To x - 1
        For j = 0 To y - 1
            outArray(i, j) = inArray(j, i)
        Next
    Next

    Transpose2dArray = outArray

End Function

The threads seem to work, because at some point after the execution of RunXTransposingThreads, I see on my screen a number of "Process complete". The question is: how do I stop the code in main from being executed if I don't have yet transposed arrays?

braX
  • 11,506
  • 5
  • 20
  • 33
Noldor130884
  • 974
  • 2
  • 16
  • 40
  • 4
    BGW is obsolete, completely replaced by `Task.Run`, `async/await` and `IProgress< T>`. It's trivial to start a lot of tasks with Task.Run and await for them to finish with `Task.WhenAll(arrayOfTasks)`. On the other hand, if you want to process a lot of data in parallel that's already available through the `Parallel` methods, eg `Parallel.ForEach` and `Parallel.For` – Panagiotis Kanavos Jan 31 '18 at 13:44
  • I read some documentation of Async/Await and making a list of tasks, but I didn't seem to be able to let the tasks give back some results. – Noldor130884 Jan 31 '18 at 13:46
  • You probably need to read the docs again then. In any case, you want parallel processing, not tasks. That's `Parallel` or PLINQ, eg `Parallel.ForEach(arrayOfArrays,Transpose2dArray)` or `arrayOfArrays.AsParallel().Select(arr=>Transpose2dArray(arr)).ToArray()` – Panagiotis Kanavos Jan 31 '18 at 13:49
  • @AndrewMorton Yeah, that's an exception, which is controlled before. As I already wrote, the loop is executed since later I get a couple of "process complete" – Noldor130884 Jan 31 '18 at 13:59
  • @AndrewMorton Not sure what you mean by that. I call `RunXTransposingThreads` just once in the `Main`. Those threads can run in parallel or async, but I do need at some point of `Main` to be sure that they produced a result. – Noldor130884 Feb 01 '18 at 06:58
  • @Noldor130884 Ah! So rather than "stop the code in main from being executed" you mean "wait for the background worker(s)" to complete. The easiest way would be use Panagiotis's suggestion of using tasks, otherwise [How to wait correctly until BackgroundWorker completes?](https://stackoverflow.com/a/1333948/1115360) would be a start. – Andrew Morton Feb 01 '18 at 09:28
  • @AndrewMorton yeah, it is actually the title of the question. Thanks for the link, I am trying with `Parallel.ForEach` and apparently that doesn't work too... – Noldor130884 Feb 01 '18 at 09:34
  • @Noldor130884 It *might* speed up the code if you swap the lines `For i = 0 To x - 1` and `For j = 0 To y - 1` as the data might be better arranged in the CPU cache. Also, for the cache, it may make your code faster overall to limit the number of BGWs to the number of physical processors or less. – Andrew Morton Feb 01 '18 at 10:02
  • @AndrewMorton Thanks again. I will try those as soon as I get the program working – Noldor130884 Feb 01 '18 at 10:10
  • 1
    @PanagiotisKanavos Would you please care to write an answer with a little bit of code in it, so I can actually understand what you mean by using `AsParallel`? C# code is okay too – Noldor130884 Feb 06 '18 at 10:44

2 Answers2

2

As others have said, BackgroundWorker is obsolete. Fortunately there are many other modern ways of doing this.

Therefore I won't show you something that makes your code works with BackgroundWorker. Instead I'll show you how to do the same thing by using Tasks, one of those modern ways. Hope it helps.

Function RunXTransposingTasks(ParamArray ArraysToTranspose() As Array) As Array
    Dim taskList = New List(Of Task(Of Single(,))) ' our tasks returns Arrays

    For Each arr In ArraysToTranspose
        Dim r = arr
        taskList.Add(Task.Run(Function() Transpose2dArray(r)))
    Next

    Task.WhenAll(taskList) ' wait for all tasks to complete. 
    Return taskList.Select(Function(t) t.Result).ToArray()
End Function

Function Transpose2dArray(inArray As Array) As Single(,)
    Dim x = inArray.GetLength(1) - 1
    Dim y = inArray.GetLength(0) - 1
    Dim outArray(x, y) As Single

    For i = 0 To x
        For j = 0 To y
            outArray(i, j) = inArray(j, i)
        Next
    Next

    Return outArray
End Function

' Usage
' Dim result = RunXTransposingTasks(Arr1, Arr2, Arr3, ...)
Kul-Tigin
  • 16,728
  • 1
  • 35
  • 64
-1

You could try using build-in function to copy data

Array.Copy(inArray, outArray, CInt(inArray.GetLength(1)) * CInt(inArray.GetLength(0)))

There's also some great example on how to use Parallel.ForEach.

the_lotus
  • 12,668
  • 3
  • 36
  • 53
  • Ok about `Array.Copy`, thanks, but I don't think that the example helps. I have am seeing tons of examples on how to use `Parallel.ForEach` in the Main, but I can't seem to find a decent example about modifying data given as input... – Noldor130884 Feb 01 '18 at 09:44