¿Es posible acelerar este código usando el bucle Parallel.For?

Tengo una lista de algunos millones de matrices de enteros y necesito eliminar todas las matrices que no cumplen con los criterios de comparación. ¿Cómo puedo usar el multihilo para acelerar el bucle a través de la Lista si necesito eliminar algunos elementos de ella?

(Este es un ejemplo de código simple, el código real tiene controles de criterios más complejos dentro del ciclo)

Function NewList(iList As List(Of Integer())) As List(Of Integer())
    Dim i As Integer
    Dim j As Integer
    Dim compareArray As Integer() = {0, 1, 2, 3, 4}
    Dim item As Integer()
    For i = iList.Count - 1 To 0 Step -1
        item = iList.Item(i)
        For j = 0 To UBound(compareArray )
            If compareArray(j) > 0 AndAlso Not item.Contains(compareArray(j)) Then
                iList.RemoveAt(i)
                GoTo nextIteration
            End If
        Next j
nextIteration:
    Next i
    Return iList
End Function
1
Oleksandr Pavlov 30 jul. 2020 a las 03:11

2 respuestas

La mejor respuesta

Intentaría algo como esto (no probado):

Public Shared Function GetFilteredList(valueList As List(Of Int32()), mustIncludeList As Int32()) As List(Of Int32())
    'Check args
    If (valueList Is Nothing) Then Throw New ArgumentNullException(NameOf(valueList))
    If (mustIncludeList Is Nothing) OrElse (Not mustIncludeList.Any()) Then Return New List(Of Int32())(valueList) 'A new instance to avoid side effects. Could be optimized if not needed
    'Normalize args
    mustIncludeList = (From e In mustIncludeList.Distinct() Where e > 0 Order By e).ToArray() 'Normalize it, remove duplicates and values < 1 (don't do it 1 million times inside the loop)
    'Initialize the filter
    Dim myFilter As Func(Of Int32(), Boolean) = Function(valueArray As Int32())
                                                    'As all of the values must be included we can skip all arrays that are shorter
                                                    If (valueArray Is Nothing) OrElse (valueArray.Length < mustIncludeList.Length) Then Return False
                                                    'Try out if the next line gives a better performance (depends a bit the size of the arrays)
                                                    Dim myValues As New HashSet(Of Int32)(valueArray)
                                                    For Each myMustInclude As Int32 In mustIncludeList
                                                        If (Not myValues.Contains(myMustInclude)) Then Return False
                                                    Next
                                                    Return True
                                                End Function
    'Apply filter and return new list
    Return valueList.AsParallel().Where(myFilter).ToList()
End Function
1
Christoph 1 ago. 2020 a las 16:37

EDITADA

Gracias a todos y especialmente por el consejo de @Christoph de asignar "Nada" a los elementos en lugar de eliminarlos y luego borrar "Nada" de la Lista. También es un consejo útil para normalizar / preparar la matriz de comparación una vez fuera del ciclo y no comprobarla millones de veces [este consejo no se implementa aquí]]

La velocidad del resultado aumentó de "minutos" a "segundos". El siguiente código devuelve una nueva lista en milisegundos con más de 2 millones de elementos.

Function NewList(iList As List(Of Integer())) As List(Of Integer())
    Dim compareArray As Integer() = {12, 15, 24}

    '//Note that "j" and "item" should be declared inside enclosed Sub 

    'Parallel.For(0, iList.Count - 1, Sub(i)
     Parallel.For(0, iList.Count, Sub(i)
    '//changed upper limit for loop /see comment below code/
                                         Dim j As Integer
                                         Dim item As Integer()
                                         item = iList.Item(i)
                                         For j = 0 To UBound(compareArray)
                                             If compareArray(j) > 0 AndAlso Not item.Contains(compareArray(j)) Then
                                                 iList(i) = Nothing
                                                 GoTo nextIteration
                                             End If
                                         Next j
nextIteration:
                                     End Sub)
    '//parallel.for looped instantly with 2,118,760 items

    '//then removing "Nothing" in usual loop have taken more than a minute,   
    '//so speed-up failed
    'Dim k As Integer
    'For k = iList.Count - 1 To 0 Step -1
    '    If iList(k) Is Nothing Then
    '        iList.RemoveAt(k)
    '    End If
    'Next

    '//but using RemoveAll Function cleared List instantly
    iList.RemoveAll(Function(itm) IsNothing(itm))

    Return iList
End Function

Probé el enfoque Parallel.For (más complejo que en mi propia respuesta) pero con parámetros de paso que se deben verificar en la función principal. Y el resultado no siempre fue correcto. Usando la función @Christoph (con AsParallel) - los resultados están bien.

Con bucle hasta iList.Count - 1: no se verificó la última lista, cambié el límite superior del bucle a iList.Count -> y los resultados son correctos. Tengo que leer la documentación con atención ... (el valor del bucle superior es exclusivo)

public static System.Threading.Tasks.ParallelLoopResult For (int 
fromInclusive, int toExclusive,
System.Threading.Tasks.ParallelOptions parallelOptions, 
Action<int,System.Threading.Tasks.ParallelLoopState> body);
0
Oleksandr Pavlov 2 ago. 2020 a las 20:03