Guía de programación OpenCL-9.1 Comandos, colas, eventos

descripción general

Las colas de comandos están en el corazón de OpenCL. Una plataforma define un contexto, que contiene uno o más dispositivos informáticos. Cada dispositivo informático puede tener una o más colas de comandos. Los comandos enviados a estas colas harán el trabajo específico del programa OpenCL.

En un programa OpenCL simple, los comandos enviados a una cola de comandos se ejecutan secuencialmente. Una vez que se completa un comando, puede comenzar el siguiente comando y el programa se expandirá en una secuencia de comandos en orden estricto. Este enfoque secuencial proporciona el rendimiento que requieren las aplicaciones cuando hay una gran cantidad de simultaneidad entre los comandos individuales.

Sin embargo, las aplicaciones reales a menudo no son tan simples. En la mayoría de los casos, las aplicaciones no necesitan ejecutar comandos de manera estrictamente ordenada. Los objetos de memoria se pueden mover entre el dispositivo y el host mientras se ejecutan otros comandos. Los comandos que procesan objetos de memoria no relacionados pueden ejecutarse simultáneamente. En aplicaciones típicas, los comandos que se ejecutan simultáneamente proporcionan suficiente simultaneidad. Esta simultaneidad puede ser explotada por el sistema de tiempo de ejecución para aumentar el paralelismo alcanzable, lo que lleva a mejoras significativas en el rendimiento.

También hay una situación común en la que las dependencias entre comandos se pueden expresar como un gráfico acíclico dirigido (DAG). Dichos gráficos pueden contener ramas independientes que se pueden ejecutar de forma segura y simultánea. Requerir que estos comandos se ejecuten en orden en serie impondría restricciones innecesarias al sistema. Una cola de comandos desordenada permite que el sistema aproveche al máximo la concurrencia entre estos comandos, pero hay más concurrencia que se puede aprovechar. Se puede aprovechar una gran cantidad de simultaneidad adicional mediante la ejecución de ramas independientes del DAG en diferentes colas de comandos que pueden estar asociadas con diferentes dispositivos informáticos.

Estos ejemplos tienen una característica común, la aplicación tiene muchas oportunidades para lograr la concurrencia, que no se puede satisfacer solo con la cola de comandos. Al relajar estas restricciones de pedido, puede haber beneficios significativos en términos de rendimiento. Sin embargo, estos beneficios tienen un precio. Si la semántica secuencial de las colas de comandos no se utiliza para garantizar un orden de ejecución de comandos seguro, esto es responsabilidad del programador. En OpenCL, esta tarea se puede realizar mediante eventos.

Los eventos son objetos en OpenCL que comunican el estado del comando. Los comandos en la cola de comandos generan eventos que otros comandos pueden esperar antes de ejecutarse. Los usuarios pueden crear eventos personalizados para proporcionar una capa adicional de control entre el host y los dispositivos informáticos. El mecanismo de eventos se puede utilizar para controlar la interacción entre OpenCL y estándares gráficos como OpenGL. Finalmente, en el kernel, los programadores usan eventos para permitir que el movimiento de datos se superponga con operaciones en esos datos.

Colas de eventos y comandos

Los eventos de OpenCL son objetos en OpenCL que transmiten información sobre los comandos. El estado del evento describe el estado del comando asociado. Se pueden tomar los siguientes valores de estado.

CL_QUEUED: 命令已经在命令队列中排队。
CL_SUBMITTED: 入队的命令由宿主机提交给与命令队列关联的设备。
CL_RUNNING: 计算设备正在执行命令。
CL_COMPLETE: 命令已经完成。
ERROR_CODE: 负值指示遇到某种错误条件。具体的值为平台或生成该事件的运行时API返回的值。

Hay muchas maneras de crear eventos. La fuente más común de eventos es el comando mismo. Cualquier comando en cola en la cola de comandos genera o espera un evento. Diferentes comandos aparecen de la misma manera en la API, por lo que podemos usar un ejemplo para explicar cómo funcionan los eventos. Considere un comando para poner en cola kernels, listo para ser ejecutado en un dispositivo informático:

cl_int clEnqueueNDRangeKernel (
    cl_command_queue command_queue,
    cl_kernel kernel,
    cl_uint work_dim,
    const size_t *global_work_offset,
    const size_t *global_work_size,
    const size_t *local_work_size,
    cl_uint num_events_in_wait_list,
    const cl_event *event_wait_list,
    cl_event *event)

Por ahora, solo estamos interesados ​​en los últimos tres parámetros de esta función.

cl_uint num_events_in_wait_list: 这个命令在执行之前需要等待完成的事件数。

const cl_event * event_wait_list: 这是一个指针数组,定义了这个命令等待的num_events_in_wait_list个事件。
                                  与event_wait_list中的事件和与command_queue关联的上下文必须相同。

cl_event * event: 这是一个指针,指向这个命令生成的一个事件对象。
                  可以由后续的命令或宿主机用来延续这个命令的状态。

Cuando se proporcionan valores legales para los parámetros num_events_in_wait_list y *event_wait_list, el comando solo se ejecutará si todos los eventos de la lista tienen el estado CL_COMPLETE o tienen un valor negativo que indica alguna condición de error.

Los eventos se usan para definir un punto de secuencia donde dos comandos ingresan a un estado conocido del programa y, por lo tanto, se pueden usar como un punto de sincronización en OpenCL. Como todos los puntos de sincronización en OpenCL, los objetos de memoria entran en un estado bien definido cuando se ejecutan varios núcleos según el modelo de memoria de OpenCL. Los objetos de memoria están asociados con un contexto, por lo que se garantiza un estado consistente incluso si los cálculos involucran varias colas de comandos en un contexto.

Por ejemplo, considere el siguiente ejemplo simple:

cl_event k_events[2];

//enqueue two kernels exposing events
err = clEnqueueNDRangeKernel(commands, kernel1, 1, NULL, &global, &lobal, 0, NULL, &k_events[0]);

err = clEnqueueNDRangeKernel(commands, kernel2, 1, NULL, &global, &lobal, 0, NULL, &k_events[1]);

//enqueue the next kernel..which waits for two prior
//events before launching the kernel
err = clEnqueueNDRangeKernel(commands, kernel3, 1, NULL, &global, &local, 2, &k_events, NULL);

Aquí 3 núcleos están en cola para su ejecución. Los primeros dos comandos clEnqueueNDRangeKernel ponen en cola kernel1 y kernel2. El último parámetro de estos comandos generará el evento, que se colocará en el elemento correspondiente del array k_events[]. El tercer comando clEnqueueNDRangeKernel pone en cola kernel3. Como muestran los parámetros séptimo y octavo de clEnqueueNDRangeKernel, kernel3 no se ejecutará hasta que se completen los dos eventos en la matriz k_events[ ]. Sin embargo, debe tenerse en cuenta que el último parámetro para poner en cola kernel3 es NULL. Esto significa que no queremos generar un evento para que accedan los comandos posteriores.

Los eventos son cruciales si necesita un control detallado sobre el orden en que se ejecutan los comandos. Sin embargo, cuando no se requiere dicho control, puede ser conveniente que los comandos ignoren los eventos (tanto su consumo como su generación). Se puede indicar a un comando que ignore los eventos utilizando el siguiente procedimiento:
1) Establezca el número de eventos que el comando está esperando (num_events_in_wait_list) en 0.
2) Establezca el puntero a la matriz de eventos (*event_wait_list) en NULL. Cabe señalar que si este puntero se establece en NULL, num_events_in_wait_list debe ser 0.
3) Establezca el puntero del evento generado (*evento) en NULL.
Este proceso garantiza que no se esperan eventos ni se generan eventos, lo que por supuesto significa que para esta instancia particular de la ejecución del núcleo, es imposible que una aplicación consulte eventos o espere a que se pongan en cola.

Al poner en cola los comandos, a menudo es deseable indicar un punto de sincronización antes del cual todos los comandos deben completarse antes de que puedan comenzar los comandos subsiguientes. Tal punto de sincronización se puede indicar en los comandos en la cola usando la función clBarrier().

cl_int clEnqueueBarrier(cl_command_queue command_queue)

Esta función tiene solo un parámetro, que define a qué cola se aplica la valla. Si la función se ejecuta correctamente, el comando devuelve CL_SUCCESS; de lo contrario, devuelve una de las siguientes condiciones de error.

CL_INVALID_COMMMAND_QUEUE: 命令队列不是一个合法的命令队列。
CL_OUT_OF_RESOURCES: 在设备上分配OpenCL实现所需要的资源时失败。
CL_OUT_OF_HOST_MEMORY: 在宿主机上分配OpenCL实现所需要的资源时失败。

El comando clEnqueueBarrier define un punto de sincronización. Esto es importante para comprender las restricciones de orden entre los comandos. Pero lo que es más importante, en el modelo de memoria OpenCL presentado, la coherencia de los objetos de memoria se define con respecto a los puntos de sincronización. Específicamente, en un punto de sincronización, las actualizaciones de los objetos de memoria visibles para los comandos deben completarse antes de que los comandos posteriores puedan ver los nuevos valores.

Para definir puntos de sincronización más generales, OpenCL usa eventos y banderas. Las banderas se configuran con los siguientes comandos.

cl_int clEnqueueMarker(
    cl_command_queue command_queue,
    cl_event *event)

cl_command_queue command_queue: 应用这个标志的命令队列
cl_event *event: 这个指针指向用来传递标志状态的事件对象

Solo después de que todos los comandos se hayan puesto en cola, se puede completar el comando de marca. Para una cola ordenada, el efecto del comando clEnqueueMarker es similar a una valla. Pero a diferencia de las vallas, los comandos de bandera devuelven un evento. El host u otros comandos pueden esperar este evento para garantizar que todos los comandos se pongan en cola antes de que se complete el comando de marca. Si la función se ejecuta correctamente, clEnqueueMarker devuelve CL_SUCCESS; de lo contrario, devuelve uno de los siguientes errores.

CL_INVALID_COMMAND_QUEUE: command_queue不是一个合法的命令队列。
CL_INVALID_VALUE: 事件是一个NULL值。
CL_OUT_OF_RESOURCES: 在设备上分配OpenCL实现所需要的资源时失败。
CL_OUT_OF_HOST_MEMORY: 在宿主机上分配OpenCL实现所需要的资源时失败。

La siguiente función pone en cola un evento, que espera a que se complete un evento específico o un grupo de eventos antes de ejecutar futuros comandos en cola.

cl_int clEnqueueWaitForEvents(
   cl_command_queue command_queue,
   cl_uint num_events,
   const cl_event *event_list)

cl_command_queue command_queue: 应用这个事件的命令队列
cl_uint num_events_in_wait_list: 这个命令等待完成的事件数
const cl_event *event_wait_list: 这是一个指针数组,定义了这个命令等待的num_events_in_wait_list个事件

Estos eventos definen puntos de sincronización. Esto significa que en el momento en que clEnqueueWaitForEvents se completa, las actualizaciones de los objetos de memoria, tal como se definen en el modelo de memoria, deben haberse completado, y los comandos posteriores pueden depender de un estado coherente de los objetos de memoria. Los eventos en event_list y el contexto asociado con command_queue deben ser iguales.

clEnqueuewaitForEvents devuelve CL_SUCCESS si la función se ejecuta correctamente; de ​​lo contrario, devuelve uno de los siguientes errores.

CL_INVALID_COMMMAND_QUEUE: command_queue不是一个合法的命令队列。
CL_INVALID_CONTEXT: 与command_queue和与event_list 中的事件关联的上下文不相同。
CL_INVALID_VALUE: num_events为0或event_list为NULL。
CL_INVALID_EVENT: event_list中指定的事件对象不是合法的事件。CL_OUT_OF_RESOURCES: 在设备上分配OpenCL实现所需要的资源时失败。CL_OUT_OF_HOST_MEMORY: 分配命令所需要的资源时失败。

Estos tres comandos clEnqueueBarrier, clEnqueueMarker y clEnqueueWaitForEvents imponen restricciones de orden en los comandos y puntos de sincronización en la cola, lo que afecta la consistencia de la memoria OpenCL. Juntos proporcionan los elementos fundamentales para los protocolos sincrónicos en OpenCL.

Por ejemplo, considere dos colas que comparten el mismo contexto, pero que dirigen comandos a diferentes dispositivos informáticos. Los objetos de memoria se pueden compartir entre los dos dispositivos (ya que comparten el mismo contexto), pero debido al modelo relajado de memoria coherente de OpenCL, en un punto dado, los objetos de memoria compartidos pueden ser relativos a una cola (u otra cola). estado indefinido. Colocar una cerca en un punto estratégico puede resolver este problema y el programador puede usar el comando clEnqueueBarrier() para resolver este problema, como se muestra en la Figura 9-1.
inserte la descripción de la imagen aquí
Sin embargo, el comando de valla en OpenCL solo impone restricciones en la cola de comandos donde se encuentra la valla, es decir, el orden de sus comandos. ¿Cómo define un programador una barrera que puede abarcar dos colas de comandos?, como se muestra en la figura 9-2.
inserte la descripción de la imagen aquí
En una de las colas, el comando clEnqueueMarker() se pone en cola y devuelve un objeto de evento válido. Una bandera actúa como una valla en su propia cola, pero también devuelve un evento que otros comandos pueden esperar. En la segunda cola, colocamos una cerca en la ubicación deseada y agregamos una llamada clEnqueueWaitForEvents detrás de la cerca. El comando clEnqueueBarrier hará que la cola correspondiente tenga el comportamiento deseado, es decir, todos los comandos antes de clEnqueueBarrier() deben completarse antes de que se puedan ejecutar los comandos posteriores. La llamada clEnqueueWaitForEvents() puede definir conexiones de otras colas a banderas. El resultado final es un protocolo de sincronización que puede definir la funcionalidad de cercado entre dos colas.

Supongo que te gusta

Origin blog.csdn.net/qq_36314864/article/details/132107633
Recomendado
Clasificación