multithreading - pthread_create - ¿Ofrece pthreads alguna ventaja sobre GCD?
pthread linux (6)
Como cualquier enfoque declarativo / asistido como openmp o Intel TBB GCD debería ser muy bueno en problemas paralelos vergonzosamente y probablemente vencerá fácilmente a los ingenuos de forma manual y paralela. Sin embargo, te sugiero que sigas aprendiendo pthreads. Comprenderás mejor la concurrencia, podrás aplicar la herramienta correcta en cada situación particular, y si no fuera por eso, hay un montón de código basado en pthread ahí fuera, podrás leer el código "heredado".
Después de haber aprendido recientemente Grand Central Dispatch, he encontrado que el código de multiproceso es bastante intuitivo (con GCD). Me gusta el hecho de que no se requieren bloqueos (y el hecho de que utiliza estructuras de datos sin bloqueo internamente) y que la API es muy simple.
Ahora, estoy empezando a aprender pthreads, y no puedo evitar estar un poco abrumado por la complejidad. Uniones de subprocesos, mutexes, variables de condición, todas estas cosas no son necesarias en GCD, pero tienen muchas llamadas de API en pthreads.
¿Ofrece pthreads alguna ventaja sobre GCD? ¿Es más eficiente? ¿Hay casos de uso normal donde pthreads pueden hacer cosas que GCD no puede hacer (excluyendo software de nivel de kernel)?
En términos de compatibilidad multiplataforma, no estoy demasiado preocupado. Después de todo, libdispatch es de código abierto, Apple ha sometido sus cambios de cierre como parches a GCC, Clang admite cierres y ya (ex FreeBSD), estamos empezando a ver algunas implementaciones de GCD que no son de Apple. Estoy más interesado en el uso de la API (¡los ejemplos específicos serían geniales!).
Ese sentimiento abrumador que estás experimentando ... es exactamente por eso que se inventó el GCD.
En el nivel más básico, hay subprocesos, pthreads es una API de POSIX para subprocesos, por lo que puede escribir código en cualquier sistema operativo compatible y esperar que funcione. GCD está construido sobre hilos (aunque no estoy seguro si realmente usaron pthreads como API). Creo que GCD solo funciona en OS X y iOS, que en pocas palabras es su principal desventaja.
Tenga en cuenta que los proyectos que hacen un uso intensivo de subprocesos y requieren un alto rendimiento implementan su propia versión de grupos de subprocesos. GCD le permite evitar (re) inventar la rueda por enésima vez.
GCD abstrae hilos y le da colas de envío. Crea subprocesos según lo considere necesario, teniendo en cuenta la cantidad de núcleos de procesador disponibles. GCD es de código abierto y está disponible a través de la biblioteca libdispatch. FreeBSD incluye libdispatch a partir de 8.1. GCD y C Blocks son contribuciones importantes de Apple a la comunidad de programación de C. Nunca usaría ningún sistema operativo que no sea compatible con GCD.
GCD es una tecnología de Apple, y no es la más compatible con varias plataformas; pthread está disponible para casi todo, desde OSX, Linux, Unix, Windows ... incluyendo esta toaster
GCD está optimizado para el paralelismo de agrupación de hilos. Los Pthreads son (como usted dijo) bloques de construcción muy complejos para el paralelismo, se le deja desarrollar sus propios modelos. Recomiendo encarecidamente elegir un libro sobre el tema si está interesado en aprender más sobre los subprocesos y los diferentes modelos de paralelismo.
Usual: 1 tarea por implementación de Pthread usa mutexes (una característica del sistema operativo).
GCD: 1 tarea por bloque, agrupada en colas. 1 subproceso por CPU virtual puede obtener una cola y ejecutarse sin exclusión mutua a través de todas las tareas. Esto reduce la sobrecarga de administración de subprocesos y la sobrecarga de exclusión mutua, lo que debería aumentar el rendimiento.
Vengo de la otra dirección: comencé a usar pthreads
en mi aplicación, que recientemente reemplacé con std::thread
C ++ 11. Ahora, estoy jugando con construcciones de alto nivel como el conjunto de subprocesos de pseudo-impulso , y aún más abstracto, los Threading Building Blocks de Intel. Yo consideraría que GCD está en o incluso más alto que TBB.
Algunos comentarios:
- imho, pthread no es más complejo que GCD: en su núcleo básico, pthread realmente contiene muy pocos comandos (solo un puñado: usar solo los mencionados en el OP le dará el 95% + de la funcionalidad que necesita). Como cualquier biblioteca de nivel inferior, es la forma en que las juntas y cómo las usas lo que te da su poder. No olvide que, en última instancia, las bibliotecas como GCD y TBB llamarán una biblioteca de subprocesos como
pthreads
ostd::thread
. - a veces, no es lo que usa, sino cómo lo usa , lo que determina el éxito frente al fracaso. Como proponentes de la biblioteca, TBB o GCD le informarán sobre todos los beneficios de usar sus bibliotecas, pero hasta que los pruebe en un contexto de aplicación real, todo esto es de utilidad teórica . Por ejemplo, cuando leí acerca de lo fácil que era usar un paralelismo finamente detallado , lo usé de inmediato en una tarea para la que pensé que podría beneficiarse del paralelismo. Naturalmente, a mí también me atrajo el hecho de que TBB manejaría todos los detalles sobre el equilibrio de carga óptimo y la asignación de subprocesos. ¿El resultado? TBB tardó cinco veces más que la versión de un solo hilo. Pero no culpo a TBB: en retrospectiva, esto es obviamente un caso de mal uso del paralelismo_for: cuando leí la letra pequeña, descubrí los gastos generales involucrados en el uso del paralelismo_for y postulé que, en mi caso, los costos del contexto la conmutación y las llamadas a funciones adicionales superaban los beneficios de usar múltiples subprocesos. Así que debes hacer un perfil de tu caso para ver cuál funcionará más rápido. Puede que tenga que reorganizar su algoritmo para usar menos sobrecarga de subprocesos.
- ¿Por qué pasó esto? ¿Cómo puede pthread o no ser más rápido que un GCD o un TBB? Cuando un diseñador diseña GCD o TBB, debe hacer suposiciones sobre el entorno en el que se ejecutarán las tareas. De hecho, la biblioteca debe ser lo suficientemente general como para que pueda manejar casos de uso extraños e imprevistos por parte del desarrollador. Estas implementaciones generales no serán gratuitas. En el lado positivo, una biblioteca consultará el hardware y el entorno de ejecución actual para hacer un mejor trabajo de equilibrio de carga. ¿Funcionará para su beneficio? La única forma de saberlo es probarlo.
- ¿Hay algún beneficio en aprender bibliotecas de nivel inferior como
std::thread
cuando hay disponibles bibliotecas de nivel superior? La respuesta es un rotundo SÍ . La ventaja de usar bibliotecas de nivel superior es la abstracción de los detalles de la implementación . La desventaja de usar bibliotecas de alto nivel también es la abstracción de los detalles de la implementación . Al usarpthreads
, soy sumamente consciente del estado compartido y de la vida útil de los objetos, porque si baje la guardia, especialmente en un proyecto de tamaño mediano a grande, puedo obtener fácilmente condiciones de carrera o fallas de memoria . ¿Estos problemas desaparecen cuando uso una biblioteca de nivel superior? Realmente no. Parece que no necesito pensar en ellos, pero de hecho, si me descuidado con esos detalles, la implementación de la biblioteca también se bloqueará. Por lo tanto, encontrará que si comprende las construcciones de nivel inferior, todas esas bibliotecas realmente tienen sentido, porque en algún momento, estará pensando en implementarlas usted mismo, si usa las llamadas de nivel inferior. Por supuesto, en ese momento, generalmente es mejor usar una llamada de biblioteca probada y depurada.
Entonces, vamos a desglosar las posibles implementaciones:
- Llamadas a la biblioteca TBB / GCD : el mayor beneficio es para los principiantes de subprocesos. Tienen barreras de entrada más bajas en comparación con el aprendizaje de bibliotecas de nivel inferior. Sin embargo, también ignoran / ocultan algunas de las trampas del uso de subprocesos múltiples. El equilibrio de carga dinámico hará que su aplicación sea más portátil sin necesidad de codificación adicional en su parte.
-
pthread
ystd::thread
calls: en realidad hay muy pocas llamadas que aprender, pero usarlas correctamente requiere atención a los detalles y un conocimiento profundo de cómo funciona su aplicación. Si puede comprender los subprocesos a este nivel, las API de las bibliotecas de niveles superiores ciertamente tendrán más sentido. - Algoritmo de un solo hilo : no olvidemos los beneficios de un segmento simple de un solo hilo. Para la mayoría de las aplicaciones, un solo hilo es más fácil de entender y mucho menos propenso a errores que los subprocesos múltiples. De hecho, en muchos casos, puede ser la opción de diseño adecuada. El hecho es que una aplicación real pasa por varias fases de subprocesos múltiples y fases de subprocesamiento único: puede que no haya necesidad de que haya subprocesos múltiples todo el tiempo.
¿Cuál es el más rápido? La verdad sorprendente es que podría ser cualquiera de los tres anteriores. Para obtener los beneficios de velocidad de los subprocesos múltiples, es posible que deba reorganizar drásticamente sus algoritmos. Si los beneficios superan o no los costos depende en gran medida del caso.
Ah, y el OP preguntó por los casos en que un thread_pool no es apropiado. Caso sencillo: si tiene un bucle estrecho que no requiere muchos ciclos para calcularlo, el uso de thread_pool puede costar más que los beneficios sin una reelaboración seria. También tenga en cuenta la sobrecarga de las llamadas a funciones, como lambda a través de grupos de subprocesos, en comparación con el uso de un solo ciclo cerrado.
Para la mayoría de las aplicaciones, los subprocesos múltiples son un tipo de optimización, así que hágalo en el momento adecuado y en los lugares correctos.