concurrency process erlang messages

concurrency - procesos erlang y arquitectura de paso de mensajes



process messages (1)

La tarea que tengo a mano es leer las líneas de un archivo grande, procesarlas y devolver resultados ordenados.

Mi algoritmo es:

  1. comience con un proceso maestro que evaluará la carga de trabajo (escrito en la primera línea del archivo)
  2. engendrar procesos de trabajo: cada trabajador leerá parte del archivo usando pread / 3, procesará esta parte y enviará los resultados al maestro
  3. el maestro recibe todos los sub-resultados, ordena y devuelve, por lo que básicamente no se necesita comunicación entre los trabajadores.

Mis preguntas:

  1. ¿Cómo encontrar el equilibrio óptimo entre la cantidad de procesos erlang y la cantidad de núcleos? entonces, si genero un proceso para cada núcleo de procesador, ¿no estaría utilizando mi CPU?
  2. ¿Cómo alcanza pread / 3 la línea especificada? ¿itera sobre todas las líneas en el archivo? y es pread / 3 un buen plan para leer archivos en paralelo?
  3. ¿Es mejor enviar un mensaje grande del proceso A al B o enviar N mensajes pequeños? He encontrado parte de la respuesta en el siguiente enlace, pero agradecería una mayor elaboración
    erlang message passing architecture

  1. Los procesos de Erlang son baratos. Eres libre (y recomendado) para utilizar más que muchos núcleos que tengas. Puede haber un límite superior para lo que es práctico para su problema (cargar 1TB de datos en un proceso por línea es pedir un poco por mucho, dependiendo del tamaño de la línea).

    La forma más fácil de hacerlo cuando no lo sabe es dejar que el usuario decida. Esto significa que podría decidir engendrar N trabajadores y distribuir el trabajo entre ellos, esperando recibir noticias suyas. Vuelva a ejecutar el programa mientras cambia N si no le gusta cómo se ejecuta.

    Maneras más difíciles de hacerlo es comparar un montón de tiempo, elegir lo que cree que tiene sentido como valor máximo, pegarlo en una biblioteca de grupo (si lo desea, un grupo de recursos preasignados, algunos para una cantidad variable), y conformarse con lo que sería una solución única para todos.

    Pero realmente, no hay una "cantidad óptima de núcleos". Puede ejecutarlo en 50 procesos, así como en 65,000 de ellos si lo desea; si la tarea es vergonzosamente paralela, la VM debería ser capaz de hacer uso de la mayoría de ellos y saturar los núcleos de todos modos.

-

  1. La lectura de archivos paralelos es una pregunta interesante. Puede o no ser más rápido (como se mencionó en los comentarios directos) y solo puede representar una aceleración si el trabajo en cada línea es lo suficientemente bajo como para que la lectura del archivo tenga el mayor costo.

    El truco es que funciones como pread/2-3 toman una compensación de bytes. Su pregunta está redactada de tal manera que le preocupan las líneas del archivo. Por lo tanto, las compensaciones de bytes que usted entrega a los trabajadores pueden terminar sobre una línea. Si tu bloque termina en la palabra my en this is my line/nhere it goes/n , un trabajador se verá a sí mismo con una línea incompleta, mientras que el otro solo informará en my line/n , omitiendo el anterior.

    En general, este tipo de cosas molestas es lo que lo llevará a tener el primer proceso de poseer el archivo y filtrarlo, solo para entregar trozos de texto para procesar a los trabajadores; ese proceso actuará entonces como una especie de coordinador.

    El aspecto agradable de esta estrategia es que si el proceso principal conoce todo lo que se envió como un mensaje, también sabe cuándo se han recibido todas las respuestas, por lo que es fácil saber cuándo devolver los resultados. Si todo es disjunto, debe confiar tanto en el titular como en los trabajadores para decirle "todos estamos sin trabajo" como un conjunto distinto de mensajes independientes que debe conocer.

    En la práctica, probablemente encontrará que lo que más ayuda es saber hacer operaciones que ayuden a la vida útil de su hardware con respecto a las operaciones de archivos, más que "cuántas personas pueden leer el archivo a la vez". Solo hay un disco duro (o SSD), todos los datos tienen que pasar por él de todos modos; el paralelismo puede ser limitado al final para el acceso allí.

-

  1. Use mensajes que tengan sentido para su programa. El programa más eficiente tendría muchos procesos capaces de funcionar sin necesidad de pasar mensajes, comunicarse o adquirir bloqueos.

    Un programa más realista y de mayor rendimiento usaría muy pocos mensajes de un tamaño muy pequeño.

    Lo divertido aquí es que su problema está intrínsecamente basado en datos. Entonces hay algunas cosas que puedes hacer:

    • asegúrese de leer el texto en un formato binario; los binarios grandes (> 64b) se asignan en un montón binario global, se comparten y se calculan mediante GC con recuento de referencias
    • Entregue información sobre lo que debe hacerse en lugar de los datos para hacerlo; este necesitaría medición, pero el proceso principal podría repasar el archivo, tener en cuenta dónde terminan las líneas, y simplemente pasar compensaciones de bytes a los trabajadores para que puedan ir y leer el archivo ellos mismos; tenga en cuenta que terminará leyendo el archivo dos veces, por lo que si la asignación de memoria no es su sobrecarga principal, es probable que esto sea más lento
    • Asegúrese de que el archivo se lea en modo raw o ram ; otros modos usan un proceso de intermediario para leer y reenviar datos (esto es útil si lee archivos a través de una red en nodos agrupados de Erlang); raw modos raw y ram da el descriptor de archivo directamente al proceso de llamada y es mucho más rápido.
    • Primero, preocúpate por escribir un programa claro, legible y correcto. Solo si es demasiado lento, debe intentar refactorizarlo y optimizarlo; es muy posible que lo encuentres lo suficientemente bueno en el primer intento.

Espero que esto ayude.

PD. Puedes probar cosas realmente simples al principio:

  1. ya sea:

    • lea todo el archivo a la vez con {ok, Bin} = file:read_file(Path) y líneas de división (con binary:split(Bin, <<"/n">>, [global]) ),
    • use {ok, Io} = file:open(File, [read,ram]) y luego use file:read_line(Io) en el descriptor de archivo repetidamente
    • use {ok, Io} = file:open(File, [read,raw,{read_ahead,BlockSize}]) y luego use el file:read_line(Io) en el descriptor del archivo repetidamente
  2. llame a rpc:pmap({?MODULE, Function}, ExtraArgs, Lines) para ejecutar todo en paralelo automáticamente (generará un proceso por línea)

  3. lists:sort/1 llamadas lists:sort/1 en el resultado.

Luego, desde allí puede refinar cada paso si los identifica como problemáticos.