none
Как переделать цикл For на Parallel.For? (VB.Net Framework 4) RRS feed

  • Вопрос

  • Есть рабочий код (Visual Studio 2010, VB.Net ), в котором встречается цикл:
    Private Sub crypt_in()
     
    Dim user As New Array3D_in
    Dim value(7) As Byte
    Dim i, iMax As Integer
    Dim permutationArrayX(11) As Byte
    Dim permutationArrayY(11) As Byte
    Dim permutationArrayZ(11) As Byte
    Dim MyArr1() As Byte
    For i = 0 To iMax - 1 Step 8
     
                    ProgressBar1.PerformStep()
                    Application.DoEvents()
                   
                    Buffer.BlockCopy(MyArr1, i, value, 0, 8)
                    user.Start(value, permutationArrayX, permutationArrayY, permutationArrayZ, ArrayXYZ)
                    value = user.MyTextNEW
                    Buffer.BlockCopy(value, 0, MyArr1, i, 8)
     
                Next
    End Sub
     
    Нужно его переделать для работы на многоядерном процессоре.
    Попробовал написать такой цикл:
     
    System.Threading.Tasks.Parallel.For(0, iMax, Sub(i_parallel)
    'ProgressBar1.PerformStep()
    'Application.DoEvents()
    If i_parallel Mod 8 = 0 Then
    'i_parallel = i_parallel + 8
    Buffer.BlockCopy(MyArr1, i_parallel, value1, 0, 8)
    user.Start(value1, permutationArrayX, permutationArrayY, permutationArrayZ, ArrayXYZ)
    value1 = user.MyTextNEW
    Buffer.BlockCopy(value1, 0, MyArr1, i_parallel, 8)
    'i_parallel = i_parallel - 8
    End If
    End Sub)
     
    Но при выполнении он выдает каждый раз новый результат!!! Т.е. выходной массив имеет разный набор значений. Если запускаю в режиме отладки, то на каких-то итерациях все получается как надо.
    Возможно это из-за того, что обращаюсь к одним и тем же переменным.
    И вторая часть вопроса: можно как-то реализовать  Step 8 для цикла Parallel.For?
     
    Спасибо, за внимание. Очень нужен ответ.
    3 июля 2011 г. 11:29

Ответы

  • Спасибо, что натолкнули на нужную мысль.

    Уже проверил код с iMax / 8, он оказался медленнее. Т.е. если в теле цикла добавлять прибавление к i_parallel, то на массивах до 1 Мб уже почти на секунду медленнее работает. А на больших объемах не проверял.

    Возможно, что этот метод лучше для малого шага. У меня Step 8. Если было бы 1000, то конечно логичней было бы воспользоваться Вашим вариантом.

    Вот привожу рабочий фрагмент кода, в котором показана реализация для For и Parallel.For

     

    Private Sub crypt_out()

    Dim user As New Array3D_out

    Dim value(7) As Byte

    Dim i, iMax, kMax As Integer

    Dim permutationArrayX(11) As Byte

    Dim permutationArrayY(11) As Byte

    Dim permutationArrayZ(11) As Byte

    Dim ArrayXYZ(11) As Byte

    'одно или много ядер?

    Dim PC As Boolean

    Dim round As Integer = ComboBox_round.SelectedIndex + 1

    Dim i_round As Integer

    If PC = False Then 'есил больше 1-го ядра

    ProgressBar1.Visible = True

    ProgressBar1.Minimum = 0

    ProgressBar1.Maximum = round

    ProgressBar1.Step = 1

    ProgressBar1.Value = 0

    Else 'если одно ядро

    ProgressBar1.Visible = True

    ProgressBar1.Minimum = 0

    ProgressBar1.Maximum = iMax * round

    ProgressBar1.Step = 8

    ProgressBar1.Value = 0

    End If

    For i_round = round To 1 Step -1

    If PC = False Then 'есил больше 1-го ядра

    ProgressBar1.PerformStep()

    System.Threading.Tasks.Parallel.For(0, iMax, Sub(i_parallel)

     

    Application.DoEvents()

    If i_parallel Mod 8 = 0 Then

    Dim user_1 As New Array3D_out

    Dim value_1(7) As Byte

     

    Buffer.BlockCopy(MyArr1, i_parallel, value_1, 0, 8)

    user_1.Start(value_1, permutationArrayX, permutationArrayY, permutationArrayZ, ArrayXYZ)

    value_1 = user_1.MyTextNEW

    Buffer.BlockCopy(value_1, 0, MyArr1, i_parallel, 8)

    End If

     

    End Sub)

    Else

    For i = 0 To iMax - 1 Step 8

     

    ProgressBar1.PerformStep()

    Application.DoEvents()

     

    Buffer.BlockCopy(MyArr1, i, value, 0, 8)

    user.Start(value, permutationArrayX, permutationArrayY, permutationArrayZ, ArrayXYZ)

    value = user.MyTextNEW

    Buffer.BlockCopy(value, 0, MyArr1, i, 8)

     

    Next

    End If

     

    Next i_round

    End Sub

     


    • Помечено в качестве ответа sg6336 5 июля 2011 г. 5:54

Все ответы

  • В поисковой строке запишите Parallel.For
    Эта тема подробно обсуждалась, правда для C# 
    3 июля 2011 г. 16:34
  • В поисковой строке запишите Parallel.For
    Эта тема подробно обсуждалась, правда для C# 

    Издеваешься?

    Твой комент значит: «долго искал? Ищи дальше!»

    3 июля 2011 г. 17:12
  • И что, ничего не нашел?
    Тогда ищи дальше,,, 
    3 июля 2011 г. 17:29
  • Прочитал все в форуме по данному запросу. Решение не нашел. Возможно Вы поможете новичку с решением данной задачи?

    В коде главная загвоздка в том, что цикл For использует один и тот же массив данных. Хотя на каждой итерации непересекающиеся фрагменты этого массива. Из массива берется 8 байт, обрабатывается и перезаписывается в новый массив такого же размера. В конкретное место.

    Путаницы не должно возникать. А на практике получается, что это чуть ли не генератор случайных чисел.

    3 июля 2011 г. 20:35
  • Разделяемый между потоками массив должен быть объявлен как Concerrent
    http://msdn.microsoft.com/ru-ru/library/dd381779#Y6671 
  • Нашел решение основной проблемы.

    Вот код, который корректно работает.

    В нем переменная временного массива и объявление объекта класса сделаны внутри цикла Parallel.For

    Объявление выходного массива не изменилось. Он объявлен вне цикла Parallel.For

     

    System.Threading.Tasks.Parallel.For(0, iMax, Sub(i_parallel)

    'ProgressBar1.PerformStep()

    Application.DoEvents()

    If i_parallel Mod 8 = 0 Then

    Dim user_1 As New Array3D_in

    Dim value_1(7) As Byte

    Buffer.BlockCopy(MyArr1, i_parallel, value_1, 0, 8)

    user_1.Start(value_1, permutationArrayX, permutationArrayY, permutationArrayZ, ArrayXYZ)

    value_1 = user_1.MyTextNEW код работает и при value_1 = user_1.MyTextNEW

    Buffer.BlockCopy(value_1, 0, MyArr1, i_parallel, 8)

    End If

    End Sub)

     

    Но, осталась не решенной проблема, как сделать шаг цикла? Т.е. For i = 0To iMax - 1 Step 8 изменить в Parallel.For.

    Мое решение слишком «бронявое» (цикл 7 раз из 8 прогоняется в пустую)

    Parallel.For(…)

    If i_parallel Mod 8 = 0 Then

    End If

    End Sub)

     

    Возможно ли более красивое решение? Т.е. можно ли Step 8 задействовать в Parallel.For?

     

    И вторая не решенная проблема:

    'ProgressBar1.PerformStep()

    При цикле Parallel.For выдает ошибку. Как обращаться к этому объекту из параллельного цикла?

     

    Спасибо, за участие и винимание

    • Снята пометка об ответе sg6336 4 июля 2011 г. 11:27
  • iMax делится нацело на 8? Тогда можно было бы делать Paraller.For от 0 до iMax/8, а потом выпонять умножение на 8 и копировать с нужного элемента. (избавились бы от лишних итераций цикла)

    К элементам пользовательского интерфейса нужно обращатся в главном (UI) потоке. Более подробно об этом можете посмотерть здесь - Практическое руководство. Осуществление потокобезопасных вызовов элементов управления Windows Forms.

     


    Для связи [mail]
    4 июля 2011 г. 13:51
  • Спасибо, что натолкнули на нужную мысль.

    Уже проверил код с iMax / 8, он оказался медленнее. Т.е. если в теле цикла добавлять прибавление к i_parallel, то на массивах до 1 Мб уже почти на секунду медленнее работает. А на больших объемах не проверял.

    Возможно, что этот метод лучше для малого шага. У меня Step 8. Если было бы 1000, то конечно логичней было бы воспользоваться Вашим вариантом.

    Вот привожу рабочий фрагмент кода, в котором показана реализация для For и Parallel.For

     

    Private Sub crypt_out()

    Dim user As New Array3D_out

    Dim value(7) As Byte

    Dim i, iMax, kMax As Integer

    Dim permutationArrayX(11) As Byte

    Dim permutationArrayY(11) As Byte

    Dim permutationArrayZ(11) As Byte

    Dim ArrayXYZ(11) As Byte

    'одно или много ядер?

    Dim PC As Boolean

    Dim round As Integer = ComboBox_round.SelectedIndex + 1

    Dim i_round As Integer

    If PC = False Then 'есил больше 1-го ядра

    ProgressBar1.Visible = True

    ProgressBar1.Minimum = 0

    ProgressBar1.Maximum = round

    ProgressBar1.Step = 1

    ProgressBar1.Value = 0

    Else 'если одно ядро

    ProgressBar1.Visible = True

    ProgressBar1.Minimum = 0

    ProgressBar1.Maximum = iMax * round

    ProgressBar1.Step = 8

    ProgressBar1.Value = 0

    End If

    For i_round = round To 1 Step -1

    If PC = False Then 'есил больше 1-го ядра

    ProgressBar1.PerformStep()

    System.Threading.Tasks.Parallel.For(0, iMax, Sub(i_parallel)

     

    Application.DoEvents()

    If i_parallel Mod 8 = 0 Then

    Dim user_1 As New Array3D_out

    Dim value_1(7) As Byte

     

    Buffer.BlockCopy(MyArr1, i_parallel, value_1, 0, 8)

    user_1.Start(value_1, permutationArrayX, permutationArrayY, permutationArrayZ, ArrayXYZ)

    value_1 = user_1.MyTextNEW

    Buffer.BlockCopy(value_1, 0, MyArr1, i_parallel, 8)

    End If

     

    End Sub)

    Else

    For i = 0 To iMax - 1 Step 8

     

    ProgressBar1.PerformStep()

    Application.DoEvents()

     

    Buffer.BlockCopy(MyArr1, i, value, 0, 8)

    user.Start(value, permutationArrayX, permutationArrayY, permutationArrayZ, ArrayXYZ)

    value = user.MyTextNEW

    Buffer.BlockCopy(value, 0, MyArr1, i, 8)

     

    Next

    End If

     

    Next i_round

    End Sub

     


    • Помечено в качестве ответа sg6336 5 июля 2011 г. 5:54