Panos Panos - 7 months ago 28
Vb.net Question

VB.NET - Parallel.For is Slower than Sequential For

I am trying to use parallel processing in order to separate data depending by its content.

In the following example I generate random numbers and I want to store them to datatables if fulfill the a condition.

For my disappointment the sequential for works faster than parallel.

Is it doable to make parallel to work faster?

Imports System.Random
Imports System.Threading
Imports System.Threading.Tasks

Public Class Form1
Public No As Integer = 5
Public DT(No) As DataTable
Public S(No) As String
Public StartTimer As DateTime
Private Sub ParrallelProc_Btn_Click(sender As Object, e As EventArgs) Handles ParrallelProc_Btn.Click
For j = 1 To No
DT(j).Rows.Clear()
Next
StartTimer = Now
For k = 1 To 10000
Parallel.For(1, No + 1, Sub(i)
Dim CurrentNo As String = CStr(Math.Round(Rnd() * 1000000, 0))
If CurrentNo.Contains(S(i)) Then DT(i).Rows.Add(CurrentNo)
End Sub)
Next
Dim Interval = Now.Subtract(StartTimer).TotalSeconds
End Sub

Private Sub SequentialProc_Btn_Click(sender As Object, e As EventArgs) Handles SequentialProc_Btn.Click
For j = 1 To No
DT(j).Rows.Clear()
Next
StartTimer = Now
For k = 1 To 10000
For l = 1 To No
Dim CurrentNo As String = CStr(Math.Round(Rnd() * 1000000, 0))
If CurrentNo.Contains(S(l)) Then DT(l).Rows.Add(CurrentNo)
Next
Next
Dim Interval = Now.Subtract(StartTimer).TotalSeconds
End Sub
End Class

djv djv
Answer Source

First off, not to brag, but my PC runs the parallel in 160ms, and the sequential in 40ms.

There is some overhead in creating threads, and with only 5 threads it's unnecessary - you might as well just do the 5 things. Especially something as lightweight as what you've got. Parallelization is made for doing multiple long-ish running tasks at the same time.

Eventually, the parallel loop is faster, once you've overcome the thread overhead. I've tested with increasing No, and this happens at around 100.

Public No As Integer = 100
Public DT(No) As DataTable
Public S(No) As String
Public StartTimer As DateTime
Private iterations As Integer = 10000

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    For i = 1 To No
        DT(i) = New DataTable()
        DT(i).Columns.Add()
        S(i) = (i + 1).ToString()
    Next
End Sub

Private Sub ParallelProc_Btn_Click(sender As Object, e As EventArgs) Handles ParallelProc_Btn.Click
    clearDT()
    Dim sw As New Stopwatch()
    sw.Start()
    For k = 1 To iterations
        Parallel.For(
            1,
            No + 1,
            AddressOf process)
    Next
    sw.Stop()
    MessageBox.Show(sw.ElapsedMilliseconds)
End Sub

Private Sub SequentialProc_Btn_Click(sender As Object, e As EventArgs) Handles SequentialProc_Btn.Click
    clearDT()
    Dim sw As New Stopwatch()
    sw.Start()
    For k = 1 To iterations
        For i = 1 To No
            process(i)
        Next
    Next
    MessageBox.Show(sw.ElapsedMilliseconds)
End Sub

Private Sub clearDT()
    For j = 1 To No
        DT(j).Rows.Clear()
    Next
End Sub

Private Sub process(i As Integer)
    Randomize()
    Dim CurrentNo As String = CStr(Math.Round(Rnd() * 1000000, 0))
    If CurrentNo.Contains(S(i)) Then DT(i).Rows.Add(CurrentNo)
End Sub

I also moved the operation to a Sub which can be called by both routines. Reuse your code not only to save time and space, but also to ensure you are just comparing the methods, not the routines.

You should also call Randomize() before using Rnd(). See https://msdn.microsoft.com/en-us/library/y66ey2hh(v=vs.110).aspx

A better test would be to put something of substance in the process() method like a Thread.Sleep(1), and play with No and iterations. You will find that sleeping in parallel is much more efficient than sleeping in sequence.