He implementado un algoritmo iterativo, donde cada iteración implica un recorrido de árbol de prepedido (a veces llamado acumulación descendente) seguido de un recorrido de árbol posterior al pedido (acumulación ascendente). Cada visita a cada nodo implica calcular y almacenar la información que se utilizará para la próxima visita (ya sea en el posterior recorrido posterior al pedido o en la iteración siguiente).¿Estrategia para implementar el algoritmo de desplazamiento de árbol en paralelo?
Durante el recorrido de preorden, cada nodo se puede procesar independientemente siempre que todos los nodos entre él y la raíz ya se hayan procesado. Después del procesamiento, cada nodo necesita pasar una tupla (específicamente, dos flotadores) a cada uno de sus hijos. En el recorrido posterior a la orden, cada nodo se puede procesar de forma independiente siempre que todos sus subárboles (si los hay) ya se hayan procesado. Después del procesamiento, cada nodo necesita pasar un solo float a su primario.
La estructura de los árboles es estática y no cambia durante el algoritmo. Sin embargo, durante el curso del recorrido descendente, si los dos flotantes que pasan son cero, no es necesario procesar todo el subárbol bajo este nodo y puede comenzar el recorrido hacia arriba para este nodo. (El subárbol debe conservarse, porque los flotantes pasados en iteraciones posteriores pueden ser diferentes de cero en este nodo y se reanudarán los recorridos).
La intensidad del cálculo en cada nodo es la misma en todo el árbol. El cálculo en cada nodo es trivial: solo unas pocas sumas y multiplicaciones/divisiones en una lista de números con una longitud igual a la cantidad de hijos en el nodo.
Los árboles que se están procesando están desequilibrados: un nodo típico tendría 2 hojas más 0-6 nodos secundarios adicionales. Entonces, simplemente particionar el árbol en un conjunto de subárboles relativamente equilibrados no es obvio (para mí). Además, los árboles están diseñados para consumir toda la RAM disponible: el árbol más grande que puedo procesar, mejor.
Mi implementación en serie alcanza el orden de 1000 iteraciones por segundo solo en mis pequeños árboles de prueba; con los árboles "reales", espero que pueda disminuir en un orden de magnitud (¿o más?). Dado que el algoritmo requiere al menos 100 millones de iteraciones (posiblemente hasta mil millones) para alcanzar un resultado aceptable, me gustaría paralelizar el algoritmo para aprovechar los múltiples núcleos. Tengo cero experiencia con programación paralela.
¿Cuál es el patrón recomendado para la paralelización dada la naturaleza de mi algoritmo?
El primer paso es analizar su algoritmo para determinar qué partes, si las hay, son independientes entre sí. Esto probablemente requiera que considere su algoritmo en un nivel inferior al que está aquí. –
¿Exactamente qué tipo de árboles está procesando y qué tipo de operaciones necesita para respaldar? Antes de siquiera considerar un cruce de árboles paralelos, pregúntese si puede volver a escribir sus árboles con una mejor estructura de datos o mejorar una operación de O (n) a O (log n). – Juliet
¿Hay datos compartidos entre los nodos del árbol, o son lógicamente separables? Puedo ofrecer algunas sugerencias decentes, pero más detalles lo harían más fácil. – Novelocrat