You are on page 1of 16

Aprender sobre varias clases de instrucciones CUDA y sus

Impacto en el comportamiento de la aplicacin


Observar la precisin relativa de la precisin simple y doble
Valores de punto flotante
Experimentar con el desempeo y la precisin de los estndares y
Funciones intrnsecas
Descubrir el comportamiento no definido de accesos de memoria inseguros
Comprender la importancia de las instrucciones aritmticas y la
Consecuencias de su uso indebido
Al tomar la decisin de usar CUDA para una aplicacin en particular, el principal motivador
Es generalmente el rendimiento computacional de las GPU. Como aprendiste en los captulos anteriores en este
Libro, con el fin de lograr un alto rendimiento en las GPUs que necesita para entender qu factores son
Limitando el rendimiento mximo. Ya ha aprendido acerca de las herramientas CUDA que pueden ayudarle a
determinar si su carga de trabajo es sensible a la latencia, el ancho de banda o las operaciones aritmticas.
Residencia en
Este entendimiento puede clasificar las aplicaciones en dos categoras:
Enlace de E / S
Computacin
En este captulo, se enfocar en el ajuste de cargas de trabajo de clculo. El rendimiento computacional
De un procesador se puede medir por el nmero de operaciones que realiza en un perodo de tiempo. Porque
Las GPU tienen instrucciones SIMT amplias y muchos ncleos computacionales, su pico computacional
El rendimiento es generalmente mucho mayor que otros procesadores.
Sin embargo, no todas las instrucciones son iguales. No importa cun rpido se ejecute su aplicacin
Si no converge en la respuesta correcta o obtiene los resultados esperados. Entendiendo el
Fortalezas y debilidades de diferentes primitivos de bajo nivel en trminos de rendimiento, precisin numrica,
Y la seguridad del hilo es importante al optimizar su aplicacin para el rendimiento y la correccin.
Saber cundo el cdigo del kernel se compila en una primitiva u otra le permite ajustar
La generacin de cdigo del compilador para satisfacer sus necesidades.
Para demostrar cmo el ajuste de la instruccin de bajo nivel puede ser beneficioso, considere el siguiente cdigo
retazo:
Valor doble = in1 * in2 + in3;
Este patrn aritmtico de una multiplicacin seguido de una adicin se llama creativamente un agregado
multiplicado, o
MAD, y es muy comn en una amplia gama de aplicaciones. Cualquier aplicacin que manipule vectores
Y matrices probablemente contiene muchas operaciones MAD como parte de la ejecucin de productos punto,
matriz
Multiplicaciones y otras funciones en lgebra lineal. Un compilador ingenuo transformara un MAD
Operacin en dos instrucciones aritmticas: una multiplicacin seguida de una adicin. Sin embargo, porque
Este patrn es tan comn, las arquitecturas modernas (incluidas las GPU NVIDIA) soportan un MAD
Instruccin que fusiona una operacin de multiplicacin y una operacin de adicin. Como resultado, el nmero de
ciclos a ejecutar
La operacin MAD se reduce a la mitad. Este rendimiento no viene de forma gratuita. Los resultados de una
La instruccin MAD es a menudo menos precisa numricamente que con instrucciones de multiplicar y agregar
separadas.
Al igual que la instruccin MAD, todos los temas cubiertos en este captulo presentarn algunas
Entre un comportamiento de aplicacin deseable y otro.
En este captulo, aprender sobre una variedad de funciones CUDA de nivel relativamente bajo que puede
Utilizar para ajustar el rendimiento, la precisin y la correccin. Usted estudiar exactamente cmo cada una de estas
caractersticas
Afecta su aplicacin en el nivel de instruccin. Al final del captulo, usted entender
Las ventajas y desventajas de los valores de punto flotante de simple y doble precisin, intrnsecos y
Funciones estndar y operaciones atmicas.
INTRODUCCIN DE LAS INSTRUCCIONES CUDA
Las instrucciones representan una sola unidad de lgica en los procesadores. Aunque puede ser raro que usted
directamente
Manipule las instrucciones mientras trabaja con CUDA, es importante que usted
Se generan diferentes instrucciones a partir del cdigo del kernel CUDA, y cmo el lenguaje de nivel superior
Las funciones se traducen en instrucciones. La eleccin entre dos instrucciones funcionalmente equivalentes puede
Afectan a una variedad de caractersticas de aplicacin, incluyendo rendimiento, precisin y correccin.
Estas preocupaciones son de particular importancia cuando se porta las aplicaciones legadas a CUDA con
Requisitos de validacin numrica. Esta seccin abarca tres temas que afectan significativamente a la
Instrucciones generadas para un kernel CUDA: operaciones de punto flotante, funciones intrnsecas y estndar,
Y operaciones atmicas. Los clculos de punto flotante operan sobre valores no integrales y afectan
Tanto la precisin y el rendimiento de los programas CUDA. Funciones intrnsecas y estndar
Superponiendo conjuntos de operaciones matemticas, pero ofrecen diferentes garantas de precisin y rendimiento.
Las instrucciones atmicas garantizan la correccin cuando se realizan simultneamente
Variable de mltiples subprocesos. Las secciones de este captulo lo involucrarn en cada uno de estos temas
Y le dar una comprensin ms profunda de sus ramificaciones para las instrucciones generadas por el compilador.
Instrucciones de punto flotante
Desde la introduccin del estndar IEEE 754 para la aritmtica de puntos flotantes,
Los proveedores de procesadores han implementado el estndar, incluyendo NVIDIA. La norma establece que
Los datos binarios de punto flotante se codifican en tres campos: un campo de signos de un bit, mltiples bits de
exponentes y
Mltiples bits que codifican el signifi cand (o fraccin), como se ilustra en la Figura 7-1.

Con el fin de garantizar un cmputo uniforme entre plataformas, IEEE-754 defi nes 32 y 64 bits
Los formatos de punto flotante, que corresponden a los tipos de datos C flotan y doblan. Su
Las representaciones tienen diferentes longitudes de bits, como se ilustra en la Figura 7-2.

Dada una variable de punto flotante de 32 bits con un bit de signo s, el exponente de 8 bits e, y el significado de 23
bits v el
El valor representado se puede calcular como se muestra en la Figura 7-3.
Es importante entender que debido a este formato estndar, las variables de punto flotante pueden
Representan valores en una granularidad ms fina que las variables enteras. Sin embargo, la exactitud numrica es
An limitado. Los valores que se pueden almacenar exactamente en formato de punto flotante son discretos y
finitos.
Por ejemplo, considere el fragmento de cdigo siguiente:
Flotar a = 3,1415927f;
Flotador b = 3,1415928f;
Si (a == b) {
Printf ("a es igual a b \ n");
} Else {
Printf ("a no es igual b \ n");
Dado que el ltimo dgito de ay b difiere, se esperara que se imprimiera:
A no es igual b
Sin embargo, en arquitecturas compatibles con el estndar IEEE 754, realmente imprimir:
A es igual a b
En este ejemplo, ninguno de los valores se puede almacenar realmente en el nmero finito de bits utilizados por el
Variables de punto flotante a y b. Como resultado, estos valores se redondean al valor ms cercano que puede
Ser almacenados, lo que pasa a ser el mismo para ambos.
Los valores de punto flotante que no se pueden almacenar correctamente se redondean a valores representables
usando una
Modo de redondeo confiable. Por ejemplo, el ejemplo anterior us el comportamiento predeterminado, roundto-
closer,
Que redondea los valores no representables al valor representable ms cercano. Otros ejemplos
De los modos de redondeo incluyen redondear a cero (siempre redondo hacia el valor con una menor absoluta
Valor), redondeo y redondeo.
Otra consideracin importante en la programacin de puntos flotantes es la granularidad de la representacin
Valores de los puntos flotantes. Como se ha expuesto, los valores de punto flotante pueden representar valores a un
mximo
Granularidad que los valores enteros. Sin embargo, slo pueden almacenar valores a intervalos discretos.
Adems, a medida que los valores de los puntos flotantes se alejan de cero (tanto en lo positivo como en lo negativo
Direccin), el intervalo entre valores representables crece tambin (como se muestra en la figura 7-4).
Puede utilizar la funcin matemtica estndar C nextafterf para encontrar el siguiente flotador representable ms
alto
Valor de un valor dado. La Tabla 7-1 muestra las diferencias entre un flotador y el siguiente
Representable para algunos valores. Obsrvese la drstica prdida de precisin cuando los valores de x aumentan.
Estos grandes intervalos entre valores de punto flotante significan que la eleccin del modo de redondeo puede
tener
Un impacto significativo en la salida numrica de cualquier aplicacin en la que puedan surgir valores extremos.
Las instrucciones que funcionan con valores de punto flotante se conocen naturalmente como instrucciones de
punto flotante.
CUDA soporta todas las operaciones aritmticas habituales para puntos flotantes tales como adicin,
Multiplicacin, divisin y sustraccin.
Como se mencion anteriormente, CUDA y otros modelos de programacin que se adhieren al IEEE 754
Niveles de precisin en el punto flotante: 32 bits y 64 bits. Estos diferentes formatos se conocen tambin como
De una sola precisin y doble precisin, respectivamente. Debido a que las variables de doble precisin usan el doble
Muchos bits como variables de precisin nica, los valores de doble precisin pueden representar correctamente
muchos ms
valores. Esto significa que el conjunto de valores de precisin doble tiene una granularidad ms fina y una mayor
Rango que los valores de precisin nica. Por ejemplo, considere una versin del punto flotante anterior
Ejemplo de precisin que utiliza flotantes de doble precisin y no de precisin simple:
Doble a = 3,1415927;
Doble b = 3,1415928;
Si (a == b) {
Printf ("a es igual a b \ n");
} Else {
Printf ("a no es igual b \ n");
}
Este cdigo produce el resultado esperado:
A no es igual b
Porque los valores representables ms cercanos para a y b no son los mismos cuando se almacenan usando la
precisin doble
Variables.
Tenga en cuenta que aunque todas las GPU NVIDIA compatibles con el procesador admiten puntos de flotacin de
precisin nica,
Necesitan una GPU NVIDIA de capacidad de clculo 1.3 o superior para utilizar valores de doble precisin.
Sin embargo, incluso los valores de doble precisin tienen sus lmites. Una seccin posterior de este captulo titulada
"SinglePrecision
Vs Double-Precision "explorar ms cuantitativamente los desafos del punto flotante
La programacin y las diferencias entre el punto flotante de precisin simple y doble.
Funciones intrnsecas y estndar
Adems de la separacin entre operaciones de precisin simple y doble, CUDA tambin categoriza
Todas las funciones aritmticas como funciones intrnsecas o estndar. Las funciones estndar se utilizan para
Las operaciones de soporte que son accesibles y estandarizadas a travs del host y el dispositivo. Estndar
Las funciones incluyen operaciones aritmticas de la biblioteca de matemticas estndar C, como sqrt, exp y sin.
Las operaciones de una instruccin como la multiplicacin y la adicin tambin se incluyen como funciones estndar.
Las funciones intrnsecas de CUDA slo se pueden acceder desde el cdigo del dispositivo. En la programacin, una
funcin
Intrnseco o incorporado, implica que el compilador tiene un conocimiento especial sobre su comportamiento, que
Permite la optimizacin ms agresiva y la generacin de instrucciones especializadas. Esto es cierto para CUDA
Funciones intrnsecas. De hecho, muchas funciones trigonomtricas se implementan directamente en hardware en
GPUs porque se usan mucho en aplicaciones grficas (para realizar traducciones, rotaciones y
As sucesivamente para aplicaciones visuales 3D).
En CUDA, muchas funciones intrnsecas estn relacionadas con una funcin estndar, lo que significa que una norma
Existe una funcin que implementa la misma operacin. Por ejemplo, la funcin estndar
La realizacin de una raz cuadrada de punto de flotar de doble precisin es sqrt. La versin intrnseca que
implementa la misma funcionalidad es __dsqrt_rn. Existe incluso una funcin intrnseca para la precisin simple
Divisin de puntos flotantes, __fdividef.
Las funciones intrnsecas se descomponen en menos instrucciones que sus funciones estndar equivalentes. Como
Un resultado, las funciones intrnsecas son ms rpidas que sus funciones estndar equivalentes pero menos
numricamente
preciso. Esto le da la capacidad de usar funciones estndar e intrnsecas intercambiablemente, pero
Producen un comportamiento diferente del programa en trminos de rendimiento y exactitud numrica.
Las funciones estndar e intrnsecas aaden una cantidad significativa de fl exibilidad a cualquier aplicacin CUDA.
Sirven como mandos de granulado fino que puede convertir para ajustar el rendimiento y precisin numrica
En una operacin por operacin. En una seccin posterior de este captulo titulada "Estndar vs. Intrnseco
Funciones "tendrs experiencia prctica ajustando estas perillas para explorar sus efectos Instrucciones atmicas
Una instruccin atmica realiza una operacin matemtica, pero lo hace en una sola operacin ininterrumpida
Sin interferencias de otros hilos. Cuando un subproceso completa correctamente un
Atmica en una variable, puede estar seguro de que el cambio de estado de la variable no ha terminado
No importa cuntos otros subprocesos tengan acceso a esa variable. Debido a que las instrucciones atmicas
impiden
Mltiples hilos de interferir entre s, permiten operaciones read-modify-write para datos
Compartidos entre los subprocesos (por ejemplo, leer el valor actual, incrementarlo y escribir el nuevo valor).
Garantizar la atomicidad de las operaciones de lectura-modificacin-escritura es especialmente importante en
situaciones altamente concurrentes
Como la GPU. CUDA proporciona funciones atmicas que realizan read-modify-write
Operaciones atmicas en 32 bits o 64 bits de memoria global o memoria compartida.
Mientras que cualquier dispositivo con capacidad de clculo 1.1 o superior soporta operaciones atmicas, basado en
Kepler
Las operaciones de memoria atmica global son ms rpidas que las operaciones basadas en Fermi, lo que
Mayor rendimiento. Esto puede permitir que las aplicaciones basadas en CUDA
Considerados impracticables para la ejecucin de la GPU debido a su gran dependencia de las operaciones atmicas.
Al igual que las funciones estndar e intrnsecas, cada funcin atmica implementa una
Operacin, como suma, multiplicacin o sustraccin. A diferencia de cualquier otro tipo de instruccin
Descritas hasta ahora, las instrucciones atmicas tienen un comportamiento defi nido cuando operan en una
ubicacin de memoria
Compartida por dos hilos competidores.
Considere el siguiente kernel simple para ayudarle a entender este concepto:
__global__ void incr (int * ptr) {
Int temp = * ptr;
Temp = temp + 1;
* Ptr = temp;
}
Este kernel lee desde una ubicacin de memoria, aade uno a ese valor y luego escribe el valor calculado
Valor a la misma ubicacin. Tenga en cuenta que no se utilizan identificadores de subproceso para cambiar las
ubicaciones de memoria
Ser accedido; Cada subproceso en un lanzamiento del kernel leer y escribir desde la misma direccin. Si un solo
Bloque de 32 subprocesos se inici con este kernel, qu producto esperara? T podras
Digamos 32: Cada hilo aumentar en uno. En verdad, el valor resultante no est definido. El problema
Aqu es causada por ms de un hilo escribiendo en la misma ubicacin de memoria. Esto se llama un
Carrera de datos o acceso inseguro a la memoria. Una carrera de datos se define formalmente como dos o ms hilos
independientes de ejecucin que acceden a la misma ubicacin, donde al menos uno de esos accesos est
modificando
Esa ubicacin. Usted no tiene manera de saber que ganar la carrera para escribir hasta que el programa es
Ejecutado. Por lo tanto, el resultado de esta o cualquier aplicacin que contiene carreras de datos es ms
Difcil de determinar.
Afortunadamente, las instrucciones atmicas evitan este comportamiento indeseable. Se accede a las instrucciones
atmicas
A travs de la API CUDA como funciones. Por ejemplo:
Int atomicAdd (int * M, int V);
La mayora de las funciones atmicas son funciones binarias, realizando una operacin en dos operandos. Toman
Como entrada una posicin de memoria M y un valor V. La operacin asociada con la funcin atmica es
Ejecutado en V y el valor ya almacenado en la posicin de memoria, * M. El resultado se escribe
Volver a la misma ubicacin de memoria.
Las funciones atmicas se pueden dividir en tres grupos: funciones aritmticas, funciones bit a bit y swap
Funciones. Las funciones aritmticas atmicas realizan aritmtica simple en la ubicacin de memoria de destino
E incluyen operaciones comunes como sumar, restar, mximo, mnimo, incremento y decremento.
Las funciones atmicas bit a bit realizan una operacin bit a bit en la ubicacin de memoria destino e incluyen
Bit a bit AND, bit a bit OR, y bit a bit XOR. El intercambio atmico funciona de forma condicional o
Intercambiar incondicionalmente el valor de una ubicacin de memoria con un nuevo valor. Funciones de
intercambio atmico siempre
Devuelve el valor originalmente almacenado en la ubicacin de destino, independientemente de si el intercambio
tiene xito.
AtomicExch reemplaza incondicionalmente el valor almacenado. AtomicCAS condicionalmente reemplaza el valor
Almacenado si el valor actualmente almacenado coincide con un valor esperado especificado por el subproceso GPU
que llama.
Como ejemplo, recuerde el ncleo de incremento anterior:
__global__ void incr (int * ptr) {
Int temp = * ptr;
Temp = temp + 1;
* Ptr = temp;
}
Puede volver a escribir el ncleo incr utilizando la funcin atomicAdd. AtomicAdd agrega atmicamente una
Valor V al valor almacenado en la posicin de memoria M. El kernel incr actualizado a continuacin utiliza una
instruccin
Para incrementar el valor almacenado en la ubicacin ptr y devuelve el valor almacenado en ptr antes de la
incremento.
__global__ void incr (__ global__ int * ptr) {
Int temp = atomicAdd (ptr, 1);
}
Con estos cambios, el comportamiento de este kernel est ahora bien defi nido. Si se lanzaron 32 subprocesos,
El valor almacenado en * ptr tendra que ser 32.
Por otro lado, qu sucede si su aplicacin no requiere que todos los subprocesos incrementen con xito
el objetivo? Qu pasa si slo le importaba si uno o algunos hilos en una urdimbre tuvo xito? Como ejemplo,
considere
El ncleo siguiente:
__glob Al__ void check_threshold (int * arr, umbral int, int * flag) {si (arr [blockIdx.x * blockDim.x + threadIdx.x]>
umbral) {* flag = 1; }}
Aqu cada hilo est comprobando su valor contra un umbral. Si ese valor est por encima del umbral,
Se fija la tasa global. Debido a que todos los subprocesos estn operando en el mismo global fl ag, si varios valores
son
Por encima del umbral, entonces la asignacin a la bandera es insegura.
Sera posible eliminar estos accesos inseguros usando atomicExch:
Int atomicExch (int * M, int V);
AtomicExch reemplaza incondicionalmente el valor almacenado en M con V y devuelve el valor anterior.
Reescribiendo el kernel check_threshold con atomicExch elimina los accesos inseguros al flag:
__global__ void check_threshold (int * arr, umbral int, int * flag) {
Si (arr [blockIdx.x * blockDim.x + threadIdx.x]> umbral) {
AtomicExch (bandera, 1);
}
}
Para este ejemplo, es realmente necesario usar atomicExch? En este caso, si usa accesos inseguros,
Todava estn garantizados que al menos un hilo escribir a * flag con xito. Usar atomicExch hace
No modificar el comportamiento de este kernel. Sera posible utilizar simplemente accesos inseguros en
Check_threshold y an tienen una aplicacin funcionalmente correcta. De hecho, el uso de atomicExch y
Otras operaciones atmicas pueden degradar signifi- cativamente el rendimiento. Sin embargo, al considerar este
tipo de
Optimizacin debe ser muy cuidadoso de que la operacin no depende realmente de los resultados de
Hilo visible. Si check_threshold realizaba un incremento como forma de contar el nmero
De valores por encima del umbral, el uso de accesos inseguros no sera vlido.
Las instrucciones atmicas son potentes en un entorno altamente paralelo como la GPU. Proporcionan un
Manera segura de operar en valores compartidos por cientos o miles de hilos. Mientras que las funciones atmicas
No sufren preocupaciones adicionales de precisin (como hacen las funciones intrnsecas), su uso puede
Degradar el rendimiento. En una seccin posterior de este captulo titulada "Entender las instrucciones atmicas"
Explorar por qu.

OPTIMIZACIN DE INSTRUCCIONES PARA SU SOLICITUD


Usted ha visto que hay muchas opciones cuando se trata de optimizar las instrucciones de su aplicacin
Usos: valores de punto flotante de una sola precisin o doble precisin, funciones estndar o intrnsecas,
Funciones atmicas o accesos inseguros. En general, cada opcin tiene compensaciones en rendimiento,
Exactitud y correccin. No hay una sola mejor opcin para todas las aplicaciones; La decisin ptima
Depende de los requisitos de su aplicacin.
En esta seccin, usted tendr la oportunidad de mirar ejemplos que demuestran las diferentes ventajas
Y las desventajas de cada clase de instruccin.
Precisin simple vs. Doble Precisin
Como se ha comentado anteriormente, los valores de precisin simple y doble difieren en el nmero de bits
utilizados para almacenar
ellos. Como resultado, las variables de doble precisin pueden representar valores a una granularidad fi nera y con
Un rango ms amplio que las variables de precisin nica. Para probar esto, puede descargar, crear y ejecutar el
Programa floating-point-accuracy.cu de Wrox.com. Este programa almacena el valor 12.1 en
Variables de precisin simple y doble en el host y el dispositivo y emite los valores reales almacenados en
20 decimales Se proporciona una salida de muestra, como sigue:
Representacin de una sola precisin de host de 12.1 = 12.10000038146972656250
Representacin de doble precisin de 12.1 = 12.09999999999999964473
Representacin de una sola precisin del dispositivo de 12.1 = 12.10000038146972656250
Representacin de doble precisin del dispositivo de 12.1 = 12.09999999999999964473
Es igual la representacin de una sola precisin del dispositivo y del host? s
Es igual la representacin de doble precisin del dispositivo y del host? s
Mientras tanto el anfitrin como el dispositivo representan el valor 12.1 con la misma aproximacin, tampoco
Capaz de almacenar precisamente ese valor. Para este ejemplo particular, los valores de doble precisin son
marginalmente
Ms cerca del verdadero valor que la simple precisin.
La exactitud de los valores de doble precisin viene con costes de espacio y rendimiento. El ejemplo
Floating-point-perf.cu de Wrox.com genera un vector de entrada de punto flotante aleatorio, copia el
Vectorial a la GPU, iterativamente realiza una serie de operaciones aritmticas en ella, y luego copia la
Resultado. Esto lo hace tanto para vectores de precisin simple como para doble precisin y mide el tiempo
requerido para
Las transferencias y para el ncleo. Todo este proceso se ejecuta repetidamente para minimizar el error de medicin
Causada por variaciones aleatorias en el tiempo de ejecucin. Se proporciona un ejemplo de salida de este programa,
como sigue:
Ejecucin de 65535 bloques con 256 threads / bloque sobre elementos 154990080
Diferencia de entrada entre simple y doble precisin
------------
0 1.16110611328622326255e-01
1 1.42341757498797960579e-01
2 1.45135404032771475613e-01
3 1.47929050144739449024e-01
4 1.03847696445882320404e-01
5 1.84766342732473276556e-01
6 1.48497488888096995652e-01
7 1.20041135203791782260e-01
8 1.38459781592246145010e-01
9 1.49065927878837101161e-01
Para punto flotante de precisin simple, los tiempos medios para:
Copiar en el dispositivo: 129 ms
Ejecucin del kernel: 574 ms
Copia del dispositivo: 201 ms
Para punto flotante de precisin doble, tiempos medios para:
Copiar en el dispositivo: 258 ms (2.00x ms lento que una sola precisin)
Ejecucin del kernel: 890 ms (1.55x ms lento que simple precisin)
Copia desde el dispositivo: 401 ms (2.00x ms lento que una sola precisin)
Este ejemplo demuestra dos cosas. En primer lugar, la diferencia de rendimiento entre precisin simple y doble
Las operaciones de punto flotante tanto en la comunicacin como en el clculo no son despreciables.
En este caso, el uso de valores de doble precisin casi duplic el tiempo total de ejecucin del programa (aunque
Su kilometraje puede variar dependiendo si su aplicacin es computacin-obligado o I / O-bound).
El tiempo para comunicar valores hacia y desde el dispositivo se duplic exactamente, simplemente porque la
precisin doble
Valores son dos veces ms largos que los valores de precisin nica. El tiempo de clculo en el dispositivo tambin
Aument tanto la cantidad de E / S de memoria global como el nmero de bits manipulados por cada uno
Instruccin aument
Este programa tambin demuestra las grandes diferencias numricas entre la precisin simple y doble
Resultados que pueden acumularse en aplicaciones iterativas como salidas imprecisas de una iteracin
Se utilizan como entradas para la siguiente iteracin. Por lo tanto, las aplicaciones iterativas requieren ms
Uso de variables de doble precisin para la precisin numrica.
Tambin es importante tener en cuenta que debido a que un valor de doble precisin ocupa el doble de espacio de
una sola precisin
Valor, cuando almacena un doble en un registro (declarado localmente en un kernel), el total compartido
El espacio de registro de un bloque de hilos se reduce ms que si se usara un flotador. Usted debe ser
extremadamente
Tener cuidado de declarar correctamente valores de precisin nicos para valores de flotador (por ejemplo pi =
3.14i59f;).
Cualquier declaracin incorrecta que omita el "f" de arrastre (pi = 3.14159) ser promovida automticamente
Por el compilador nvcc a doble precisin.
Resumen
Los efectos de las operaciones de punto flotante sobre el rendimiento de las aplicaciones y la exactitud numrica son
No exclusivo de las GPU; Se enfrentan a las mismas preocupaciones cuando se trabaja con otras arquitecturas. Sin
embargo,
CUDA y GPUs tienen estas cualidades nicas: Se agreg la comunicacin del dispositivo host con valores de doble
precisin
Adicin de E / S de memoria global con valores de doble precisin
Prdida de precisin numrica debido a optimizaciones agresivas del punto flotante por el CUDA
compilador
En general, si la precisin es primordial para su aplicacin, entonces los valores de doble precisin son
imprescindibles.
De lo contrario, los valores de precisin nica pueden ayudarle a obtener beneficios de rendimiento. La Tabla 7-2
resume
Las lecciones aprendidas usando operaciones de punto flotante en CUDA.

actuacion exactitud presicion


PRECISIN SIMPLE Bueno; Slo utiliza 32 Ningn cambio; sin
PUNTO FLOTANTE Mejor; Menos Bits para almacenar proteccin
comunicacin
valores; Contra mltiples hilos
Y ligeramente
Computacin mejorada No slo es el rango Accesos inseguros
Rendimiento De mnimo y mximo
Valores ms pequeos
Sino tambin la
granularidad
A qu valores
Puede representarse es
Mayor
PRECISIN DOBLE Bueno; Como resultado Mejor; gama ms amplia Ningn cambio; sin
PUNTO FLOTANTE del doble de los bits que As como mejorado proteccin
tienen que ser Precisin gracias a 64 Contra mltiples hilos
transferidos a la GPU y Bits de almacenamiento Accesos inseguros
valores ms amplios que
se estn operando en

Funciones estndar vs intrnsecas


Las funciones estndar e intrnsecas difieren tanto en la exactitud numrica como en el rendimiento. Funciones
estndar
Proporcionan un soporte completo para una amplia gama de operaciones aritmticas. Sin embargo, muchos tienen
Funciones intrnsecas que implementan la misma funcionalidad pero con menos instrucciones,
Rendimiento y menor precisin numrica.
Visualizacin de funciones estndar e intrnsecas
Una forma de visualizar la diferencia entre funciones estndar e intrnsecas es estudiar las instrucciones
Generado por el compilador CUDA para cada funcin. Utilizando el comando --ptx fl ag con nvcc
El compilador para generar una representacin intermedia del programa en el hilo paralelo
Ejecucin (PTX) Arquitectura del conjunto de instrucciones (ISA), en lugar de un ejecutable final. PTX es similar
A ensamblaje en programacin x86; Proporciona una representacin intermedia entre el ncleo
Cdigo que escriba y las instrucciones ejecutadas por la GPU. Como tal, es til para ganar
Informacin sobre la ruta de ejecucin de bajo nivel de un kernel.
Como ejemplo, puede generar el PTX para las siguientes dos funciones CUDA para comparar visualmente
Estndar y funciones intrnsecas. Para ello, guarde estos kernels en un archivo llamado foo.cu:
__global__ void intrinsic (float * ptr) {
* Ptr = __powf (* ptr, 2.0f);
}
__global__ void estndar (float * ptr) {
* Ptr = powf (* ptr, 2,0f);
}
A continuacin, genere un archivo PTX llamado foo.ptx utilizando el siguiente comando:
$ Nvcc --ptx -o foo.ptx foo.cu
El compilador nvcc generar un archivo que contiene las instrucciones PTX para ambas funciones del dispositivo.
Puede abrir foo.ptx con su editor de texto favorito.
El contenido de foo.ptx puede ser un poco confuso si no has visto instrucciones crudas
antes de. La primera instruccin a buscar es la instruccin de propsito especial que marca la
Comienzo de una definicin de funcin. Debido a que haba dos ncleos en foo.cu, hay dos .entry
Instrucciones en el archivo PTX generado. Con CUDA 5.0, la firma de la funcin para el estndar
Funcin es:
.entry _Z8standardPf (
.param .u64 __cudaparm__Z8standardPf_ptr)
{
...
}
Y para la funcin instrnseca es:
.entry _Z9intrinsicPf (
.param .u64 __cudaparm__Z9intrinsicPf_ptr)
{
...
}
Los nombres de las funciones pueden variar dependiendo de la versin del compilador. Lo que debera ser familiar
Son la abrazadera inmediatamente despus de la entrada y un corchete de cierre correspondiente ms adelante en el
archivo. Slo
Como en C, estos corchetes contienen las instrucciones que definen la lgica de cada funcin. Por ejemplo, el
La primera funcin defi nida en su archivo foo.ptx es la versin intrnseca y debe ser similar a:
.entry _Z9intrinsicPf (
.param .u64 __cudaparm__Z9intrinsicPf_ptr)
{
.reg .u64% rd <3>;
.reg .f32% f <7>;
.loc 14 4 0
$ LDWbegin__Z9intrinsicPf:
.loc 14 5 0
Ld.param.u64% rd1, [__cudaparm__Z9intrinsicPf_ptr];
Ld.global.f32% f1, [% rd1 + 0];
Lg2.approx.f32% f2,% f1;
Mov.f32% f3, 0f40000000; // 2
Mul. F _ {32} f _ {4},% f _ {2},% f _ {3};
Ex2.approx.f32% f5,% f4;
St.global.f32 [% rd1 + 0],% f5;
.loc 14 6 0
salida;
$ LDWend__Z9intrinsicPf:
} // _Z9intrinsicPf
Se necesitan 17 lneas para implementar la funcin __powf intrnseca, y slo siete instrucciones para realizar
Una exponenciacin de punto flotante. Sin embargo, si echa un vistazo a la funcin powf estndar
Defi ned en su archivo foo.ptx, es probable mucho, mucho ms tiempo (344 lneas utilizando el CUDA 5.0 Toolkit).
Mientras que el recuento de lneas no se traduce directamente en instrucciones o ciclos, se puede imaginar el ahorro
en
Rendimiento son todava significativos.
Sin embargo, no es slo el rendimiento que separa funciones estndar e intrnsecas; Tambin difieren
En la precisin. Para probar las diferencias en rendimiento y precisin, puede descargar, crear,
Y ejecute el ejemplo intrinsic-standard-comp.cu de Wrox.com. El kernel en este programa
Calcula repetidamente el cuadrado de un valor de entrada, primero usando la funcin estndar powf, luego
La funcin intrnseca __powf. Este ejemplo tambin utiliza la biblioteca de matemticas estndar C en el host para
Realizar el mismo clculo y utiliza los resultados del host como un valor de lnea de base. Una muestra de salida de
Intrinsic-standard-comp.cu es la siguiente:
Host calculado 66932852.000000
Dispositivo estndar calculado 66932848.000000
Dispositivo intrnseco calculado 66932804.000000
Host igual a Standard? No diff = 4.000000e + 00
Host es igual a Intrnseco? No diff = 4.800000e + 01
Estndar es igual a Intrnseco? No diff = 4.400000e + 01
Tiempo de ejecucin medio para la funcin estndar powf: 47 ms
Tiempo medio de ejecucin de la funcin intrnseca __powf: 2 ms
Como era de esperar, hay grandes ganancias de rendimiento utilizando funciones intrnsecas sobre funciones
estndar,
Con casi 24 veces la aceleracin. Los resultados numricos son mucho ms interesantes. No slo son los
Salidas de la norma CUDA y funciones intrnsecas numricamente diferentes, pero tambin
Difieren del resultado calculado por la biblioteca de matemticas estndar del anfitrin. Sin embargo, al comparar el
Resultado intrnseco y el resultado estndar, el resultado intrnseco es un orden de magnitud ms
Resultado del host base.
PUESTO DE CPU A GPU
Utilizando CUDA para simulaciones cientficas, algoritmos fi nancieros y otras aplicaciones
Que exigen un alto nivel de exactitud y fidelidad generalmente requiere dos pasos:
Portar aplicaciones heredadas de un framework solo de CPU a CUDA, seguido por
Verificar la exactitud numrica del puerto comparando los resultados del
Implementacin y la versin CUDA.
Incluso cuando se utilizan funciones CUDA numricamente estables, los resultados del clculo
De los dispositivos GPU pueden diferir de las aplicaciones de la CPU
reemplazar. Debido a las inexactitudes inherentes a las operaciones de punto flotante en ambos
El anfitrin y el dispositivo, a veces puede ser difcil sealar que una salida es ms
Correcto que el otro. Por lo tanto, los planes de portabilidad deben prepararse explcitamente para
Diferencias y, si es necesario, establecer tolerancias aceptables.
Manipulacin de la generacin de instrucciones
En la mayora de los casos, la traduccin del cdigo del kernel escrito por el programador al conjunto de instrucciones
de la GPU es
Manejado detrs de las escenas por el compilador CUDA. Rara vez usted tendr el deseo de inspeccionar o
Manualmente las instrucciones que se generan. Sin embargo, eso no significa que no pueda
Dirija fcilmente al compilador para que prefiera el rendimiento o la precisin, o equilibre ambos. Dos tcnicas
permiten
Control ms estricto sobre los tipos de optimizaciones a nivel de instruccin que el compilador CUDA puede realizar:
Las lneas de compilacin y las llamadas de funcin intrnsecas o estndar.
Por ejemplo, la funcin intrnseca __fdividef implementa ms rpido pero menos precisa numricamente
Divisin de puntos flotantes, en comparacin con el operador /. Supongamos que tiene el siguiente ncleo del
ncleo:
__global__ void foo (...) {
Float a = ...;
Float b = ...;
Flotante c = a / b;
}
Usted podra simplemente reemplazar / con el funcionalmente equivalente __fdividef y medir mejor
actuacin:
__global__ void foo (...) {
Float a = ...;
Float b = ...;
Float c = __fdividef (a, b);
}
Sin embargo, ajustar manualmente la operacin del ncleo por operacin es muy intensiva en mano de obra.
Copiadoras del compilador
Proporcionan una manera ms automatizada y global de manipular la generacin de instrucciones del compilador.
por
Por ejemplo, es posible que desee controlar la generacin de la instruccin MAD (FMAD) de punto flotante
Por el compilador CUDA. Recordemos que MAD es una simple optimizacin del compilador que fusiona una
multiplicacin Y aadir en una sola instruccin, reduciendo as a la mitad el tiempo que la operacin tomara en
comparacin con
Utilizando dos instrucciones. Sin embargo, esta optimizacin viene a costa de alguna precisin numrica.
Por lo tanto, algunas aplicaciones podran querer limitar explcitamente el uso de la instruccin FMAD.
La opcin --fmad a nvcc globalmente habilita o deshabilita la optimizacin FMAD para una compilacin completa
unidad. De forma predeterminada, nvcc utiliza --fmad = true para habilitar la generacin de instrucciones FMAD
como un rendimiento
mejoramiento. Pasar --fmad = false evita que el compilador fusione cualquier multiplicacin con adiciones,
Perjudicando el rendimiento pero probablemente mejorando la precisin numrica de su aplicacin.
Por ejemplo, dado el siguiente kernel simple:
__global__ void foo (float * ptr) {
* Ptr = (* ptr) * (* ptr) + (* ptr);
}
Generar el PTX para foo con --fmad = true produce una sola instruccin aritmtica para el cuerpo del kernel:
Mad.f32% f2,% f1,% f1,% f1;
Aqu se puede ver una sola instruccin MAD de multiplicacin-adicin aplicada a tres puntos de flotacin de 32 bits
Valores, como se esperaba. Si este kernel es compilado con --fmad = false, un par diferente de instrucciones
Aparecen en el lugar de la instruccin MAD:
Fm%,% f _ {1},% f _ {1};
Add.rn.f32% f3,% f2,% f1;
El compilador funcion como se esperaba; Usted puede ver que nvcc no fusion el multiplicar y agregar
Juntos en una sola instruccin MAD.
Observe que hay una serie de paquetes de compiladores CUDA que afectan la generacin de instrucciones
aritmticas,
Adems --fmad. Una lista completa de ellos est disponible con la opcin --help a nvcc, y est listada en
Tabla 7-3.
descripcion Valor por defecto Efecto sobre el Efecto en la
BANDERA rendimiento exactitud

--ftz=[true, false] Enjuaga todas las false Cuando se establece Cuando se establece
Punto flotante en true, en
denormal Esta bandera podra Falsa, esta bandera
Valores a cero. Una mejorar el Podra mejorar
descripcion rendimiento, exactitud,
De los nmeros dependiente dependiente
denormales Sobre los valores Sobre los valores
Est fuera del Procesado y Procesado y
alcance de Aritmtica realizada Aritmtica realizada
Este libro, pero su en tus en tus
presencia solicitud. solicitud.
En su aplicacin
Puede requerir todo
o algunos
Operaciones
aritmticas para
Tomar cdigo menos
eficiente
Caminos
(dependiendo de
Si tienes un
preFermi
O GPU post-Fermi).
--prec-div= Mejora el nmero true Cuando se establece Cuando se establece
[true,false] Exactitud de toda la en true, en
precisin nica Podra haber Verdadero,
Divisiones y actuacin numrico
Reciprocos degradacin compatibilidad
con el
Norma IEEE
Mejora
--prec-sqrt= Fuerzas true Cuando se establece Cuando se establece
[true,false] numricamente ms en true, en
Raz cuadrada exacta Podra haber Verdadero,
funcin actuacin numrico
degradacin compatibilidad
con el
Norma IEEE
Mejora
--fmad=[true,false] Controla si el true Habilitacin de Habilitacin de
El compilador est FMAD FMAD
autorizado a Mejora el Podra reducir
Fusionar rendimiento, La exactitud de
operaciones de previsto su aplicacin.
multiplicar y agregar Hay loco
En una sola FMAD Operaciones en
instruccin punto flotante
Variables en su
solicitud.
--use_fast _math Reemplaza todos los false Habilitacin --use_ Habilitacin --use_
Funciones en su Traje rpido Traje rpido
aplicacin Implica un nmero Podra disminuir
Con su intrnseco De optimizaciones El numrico
Funcin Habilitado, todos Exactitud de su
equivalentes. Eso De los cuales solicitud.
Tambin establece mejoran
--ftz = true, actuacin.
--prec-div = false, y
--prec-sqrt = false

Adems de la opcin --fmad, CUDA tambin incluye un par de funciones intrnsecas que se pueden utilizar
Para controlar la generacin de la instruccin FMAD: __fmul y __dmul. Estas funciones implementan el punto
flotante
Multiplicacin para los tipos flotante y doble. Si bien estas funciones no afectan el rendimiento
De la operacin de multiplicacin, llamndolos en el lugar de un operador * impide nvcc
De utilizar esa multiplicacin como parte de la optimizacin MAD. Por ejemplo, en el cdigo anterior
Sample of the kernel foo, --fmad = false se us para evitar la generacin de una instruccin mad.f32. los
El mismo efecto podra haber sido logrado insertando una llamada a __fmul:
__global__ void foo (float * ptr) {
Ptr = __fmul_rn (* ptr, * ptr) + * ptr;
}
Tenga en cuenta que __fmul y __dmul impiden la generacin de instrucciones MAD independientemente de si
--fmad = true o --fmad = false se especifica. Como resultado, es posible tener habilitada la optimizacin del
compilador MAD globalmente, al tiempo que mejora la robustez numrica de ciertos clculos por
Llamando selectivamente __fmul o __dmul.
Usted puede haber notado que cuando __fmul fue llamado en foo, la llamada fue en realidad a una funcin
__fmul_rn. Muchas funciones intrnsecas de punto flotante (incluyendo __fadd, __fsub, __fmul, etc.)
Indicar explcitamente el modo de redondeo de punto flotante en el nombre de la funcin utilizando un sufijo de dos
caracteres
X (resumido en la Tabla 7-4). Recurdese que debido a que las variables de punto flotante todava pueden
representar
Discretos aunque de grano fino, cualquier valor irrepresentable debe ser redondeado a valores representables.
El modo de redondeo de una operacin de punto flotante determina cmo se convierten los valores irrepresentables
A valores representables.

Sufij sentido
o
Rn Valores redondeados que no se pueden representar con precisin en el punto flotante actual
(Simple o doble) al valor ms cercano que se pueda representar. Esto es
El comportamiento predeterminado.
Rz Los valores siempre redondos hacia cero (es decir, los valores mayores que cero son redondeados
Abajo y los valores menos que cero se redondean hacia arriba).
Ru Siempre redondea los valores hacia el infinito positivo.
rd Siempre redondea los valores hacia abajo hacia el infinito negativo

Ahora que ha visto los cambios de nivel de instruccin causados por la habilitacin o deshabilitacin de la FMAD
Optimizacin, tambin se puede observar el efecto de estos cambios en la exactitud numrica. T
Puede descargar, construir y ejecutar el ejemplo de fmad.cu desde Wrox.com. Este ejemplo ejecuta un solo
Operacin MAD en el host y una sola operacin MAD en el dispositivo usando funciones estndar.
Compilar fmad.cu con diferentes valores para el --fmad fl ag le permite comparar los resultados de
Kernels CUDA que se ejecutan con y sin la optimizacin MAD con respecto a un valor de
anfitrin.
Primero, intente compilar fmad.cu con la optimizacin MAD CUDA activada. Tenga en cuenta que tambin podra
Deje el parmetro --fmad = true fuera, ya que por defecto es true.
$ Nvcc -arch = sm_20 --fmad = true fmad.cu -o fmad
La ejecucin de la aplicacin generada produce la siguiente salida de ejemplo:
F
El dispositivo emite un valor diferente del host, diff = 8.881784e-16.
Como era de esperar, utilizando la optimizacin MAD llev a pequeos errores numricos en el dispositivo. Fmad.cu
puede
Tambin se compilar con la optimizacin del compilador MAD explcitamente deshabilitada:
$ Nvcc -arch = sm_20 --fmad = false fmad.cu -o fmad La ejecucin de la aplicacin deshabilitada MAD produce la
siguiente salida de ejemplo:
F
El dispositivo emite el mismo valor que el host.
Con FMAD deshabilitado, los valores producidos por el host y el dispositivo son idnticos. Sin embargo, el dispositivo
El kernel ahora requerir ms instrucciones para realizar este clculo.
Resumen
Esta seccin demostr el amplio y omnipresente impacto que los estndares e intrnsecos
Funciones del programa (como se resume en la Tabla 7-5). En muchas situaciones, puede controlar
Cmo se generan las instrucciones por el compilador, lo cual es extremadamente til cuando
Aplicacin para el rendimiento y precisin numrica.
Descripcin de las instrucciones atmicas
En esta seccin, usted explorar cmo usar las operaciones atmicas y aprender sobre la implementacin correcta
Operaciones en datos compartidos en un entorno altamente concurrente. Tenga en cuenta que las GPUs de
diferentes
Capacidad de apoyo diferentes funciones atmicas. Necesitar tener acceso a una GPU con capacidad de clculo
1.0 o superior para ejecutar los ejemplos en esta seccin.
Desde el principio
Cada funcin atmica proporcionada por CUDA puede ser reintroducida usando una sola funcin atmica:
Operador de comparacin y intercambio atmico (CAS). Atomic CAS es una operacin poderosa que no slo
Le permiten defi ne sus propias funciones atmicas en CUDA, sino tambin ayuda con una comprensin ms
profunda
De las operaciones atmicas en general.
CAS toma como entrada tres elementos: Una ubicacin de memoria, un valor esperado en esa ubicacin de memoria
y
El valor que desea almacenar en esa ubicacin de memoria. A continuacin, realiza los siguientes pasos:
1. Lea la ubicacin de destino y compare el valor almacenado all con el valor esperado.
a. Si el valor almacenado es igual al valor esperado, la ubicacin de la memoria objetivo se llena con
El valor deseado.
segundo. Si el valor almacenado no es igual al valor esperado, entonces no se realiza ningn cambio en el valor
ubicacin del objetivo.
2. En cualquier caso, una operacin CAS devuelve siempre el valor que encontr almacenado en el destino
ubicacin. Tenga en cuenta que al utilizar el valor devuelto, puede comprobar si hay un intercambio satisfactorio. Si el
valor
Devuelto es igual al valor esperado pasado, entonces la operacin CAS debe haber tenido xito.
Ahora, eso es slo la operacin CAS. Un CAS atmico implica que todo el proceso CAS se realiza
Sin interferencia de otros hilos. Porque es un operador atmico, si el valor de retorno de
La operacin CAS indica que la escritura tuvo xito, entonces ese cambio se debe haber hecho visible
A todos los otros hilos tambin.
Para aprender ms sobre las operaciones atmicas, ser til para usted implementar un ejemplo atmico
Funcin desde el suelo utilizando la funcin de dispositivo CUDA atomicCAS. Para este ejemplo, usted ser
Implementando la adicin entera atmica de 32 bits. La firma de la variante pertinente de atomicCAS es:
Int atomicCAS (int * address, int compare, int val);
Aqu direccin es la ubicacin de la memoria de destino, comparar es el valor que se espera que en esa ubicacin,
Y val es el valor que desea escribir en esa ubicacin.
Entonces, cmo podras implementar una adicin atmica usando atomicCAS? Primero tendr que descomponer
Adicin en sus componentes y defi nindolo como una operacin CAS. Una tcnica til
Cuando la implementacin de operaciones atmicas personalizadas es defi nir los estados de inicio y finalizacin del
objetivo.
Con un agregado atmico, el estado inicial es el valor base que se incrementar. El acabado
Estado es la suma del estado inicial y el valor de incremento. Esta definicin se traduce directamente a
AtomicCAS: El valor esperado es el estado inicial y el valor deseado es el estado final.
Para implementar una funcin de adicin atmica personalizada, se iniciar con una firma de funcin que
Una ubicacin para agregar y un valor para aadir a ella:
__device__ int myAtomicAdd (int * direccin, int incr) {
...
}
Puede calcular un valor esperado para el objetivo simplemente leyendo la ubicacin de memoria de destino.
El valor deseado es defi nido por el valor ledo ms el valor incre que se pasa a myAtomicAdd.
Usando estos valores esperados y deseados, se puede hacer una llamada a atomicCAS que implemente una
adicin:
__device__ int myAtomicAdd (int * direccin, int incr) {
// Crea una conjetura inicial para el valor almacenado en * address.
Int esperado = * direccin;
Int oldValue = atomicCAS (direccin, esperado, esperado + incr);
...
}
Esta funcin myAtomicAdd ya puede realizar un atmico agregar! Sin embargo, la operacin slo
Tener xito si el valor ledo en esperado es tambin el valor almacenado en la direccin cuando el atomicCAS es
Realizado. Debido a que la ubicacin de destino es compartida por mltiples subprocesos (de lo contrario, las
operaciones atmicas
No es necesario), es posible que otro hilo modifique el valor en la direccin entre
Siendo ledo en esperado y modificado por atomicCAS. Si eso sucediera, la atomicCAS
Fallan porque el valor actual en la direccin y el valor de esperado seran diferentes.
Recuerde que el fallo es sealado por un valor de retorno atomicCAS que difiere del valor esperado.
Utilizando esta informacin, myAtomicAdd puede comprobar si hay un error y repetir la comparacin y el
intercambio en un
Hasta que atomicCAS tenga xito:
__device__ int myAtomicAdd (int * direccin, int incr) {
// Crea una conjetura inicial para el valor almacenado en * address.
Int esperado = * direccin;
Int oldValue = atomicCAS (direccin, esperado, esperado + incr);
// Loop while expected es incorrecto.
While (oldValue! = Esperado) {
Expected = oldValue;
OldValue = atomicCAS (direccin, esperado, esperado + incr);
}
Return oldValue;
}
Las tres primeras lneas de esta funcin son las mismas que las anteriores. Si falla la primera atomicCAS,
MyAtomicAdd ahora loops mientras el ltimo valor devuelto por atomicCAS sea diferente del
valor esperado. Una vez que esa condicin falla, el intercambio debe haber tenido xito, y myAtomicAdd sale
el lazo. De lo contrario, el valor esperado se restablece al valor ms reciente ledo y se realiza un reintento. A
Coincide con la semntica de otras funciones atmicas CUDA, myAtomicAdd tambin devuelve el valor que fue
Reemplazado en la ubicacin de destino devolviendo el valor ms reciente devuelto por atomicCAS.
Puede descargar, crear y ejecutar una copia de este cdigo en my-atomic-add.cu desde Wrox.com. Construir
como sigue:
$ Nvcc -arch = sm_11 my-atomic-add.cu
Aunque la siguiente seccin cubre las funciones atmicas que CUDA apoya nativamente, es importante
Para entender que usted no est limitado a ellos. Con atomicCAS puedes implementar una
Gama de operaciones atmicas segn lo requiera su aplicacin particular.
Funciones atmicas CUDA incorporadas
CUDA soporta una coleccin de funciones atmicas. El subconjunto de los accesibles depende de
La capacidad de clculo de su dispositivo.
El soporte para funciones atmicas comienza en la capacidad de clculo 1.1. En este nivel, usted tendr acceso a
Funciones que manipulan valores de 32 bits en la memoria global.
Compatibilidad con la manipulacin de valores de 32 bits en memoria compartida y valores de 64 bits en memoria
global
Con capacidad de clculo 1.2. Soporte para manipulaciones de 64 bits en memoria compartida comienza con
Capacidad de clculo 2.0.
La Tabla 7-6 enumera las operaciones para las que CUDA apoya una funcin atmica, la CUDA asociada
La funcin del dispositivo y los tipos soportados.

El costo de las operaciones atmicas


Aunque las funciones atmicas son muy tiles y absolutamente necesarias en algunas aplicaciones, pueden
Vienen con un alto costo de rendimiento. Hay una serie de factores que contribuyen a esto:
1. Al realizar una operacin atmica en memoria global o compartida, una de las garantas
Hecho por la atomicidad es que el cambio ser inmediatamente visible para todos los hilos. Por lo tanto, en
Mnimo, una instruccin atmica va hasta la memoria global o compartida para
Leer el valor actual almacenado all sin permitir el almacenamiento en cach. Si la instruccin atmica tiene xito,
Tambin debe escribir el valor deseado en la memoria global o compartida.
2. Confirmar los accesos atmicos a una ubicacin compartida podra requerir uno o ms reintentos
Conflictivo, anlogo a ejecutar ms de una iteracin del bucle de myAtomicAdd.
Aunque hay una visibilidad limitada de cmo se construyen las funciones atmicas incorporadas, esto
Ciertamente es cierto para cualquier operacin atmica personalizada que implemente. Si su aplicacin loops
Repetidamente mientras incurra en gastos generales de E / S, el rendimiento se degradar.
3. Cuando los hilos en la misma urdimbre deben ejecutar instrucciones diferentes, la ejecucin de urdimbre se
serializa.
Si varios hilos en una urdimbre emiten una operacin atmica en la misma ubicacin en memoria, algo
Similar suceder cuando se mezclan entre s. Debido a que slo un hilo atmico
Operacin puede tener xito, todos los dems deben volver a intentarlo. Si una sola instruccin atmica requiere n
ciclos, y
T en la misma urdimbre ejecutar esa instruccin atmica en la misma ubicacin de memoria, entonces
El tiempo transcurrido ser t n, ya que slo un hilo tiene xito en cada repeticin sucesiva. Mantener dentroMente
que el resto de los hilos en la urdimbre tambin se estancan esperando todos los atmicos
Operaciones completas, y que una operacin atmica tambin implica una lectura y escritura globales.
Para explorar las instrucciones atmicas, usted estar examinando algunos ejemplos simples. En primer lugar, es
interesante
Para comparar el comportamiento y el rendimiento de las operaciones atmicas con accesos inseguros. T puedes
descargar,
Construir y ejecutar atomic-ordering.cu desde Wrox.com. Esta pequea aplicacin contiene dos ncleos,
Llamado atomics e inseguro. El ncleo atmico realiza adiciones atmicas de cada hilo
En una sola variable compartida, guardando el valor antiguo en la ubicacin de destino:
Values_read [tid] = atomicAdd (shared_var, 1);
El kernel inseguro realiza las mismas adiciones en la misma variable compartida, pero no utiliza
Funciones atmicas:
Int old = * shared_var;
* Shared_var = old + 1;
Values_read [tid] = antiguo;
Eso significa que los hilos que ejecutan el kernel inseguro estn realizando lecturas y escrituras globales sin
Cualquier mecanismo para evitar la sobreescritura mutua. Debido a que ambos ncleos tambin almacenan los
valores antiguos
En el lugar de destino, los conflictos de hilos pueden visualizarse como valores antiguos duplicados. Una muestra de
salida
es como sigue:
En total, 30 recorridos con operaciones atmicas tomaron 3704 ms
El uso de operaciones atmicas tambin produjo una salida de 6400064
En total, 30 carreras con operaciones inseguras tomaron 11 ms
El uso de operaciones inseguras tambin produjo una salida de 100001
Hilos que realizan operaciones atmicas leer valores 1 3 5 7 17 19 21 23 33 35
Hilos que realizan operaciones inseguras leer valores 0 0 0 0 0 0 0 0 0 0
La diferencia en el rendimiento es obvia: la versin que usa los atmicos tom ms de 300 veces
correr. La salida final muestra que no todas las adiciones realizadas en la versin insegura son
Escrito en la memoria global; Muchos se sobrescriben y nunca se leen por otros hilos. Estos conflictos
Se hacen ms evidentes por los valores listados en las dos ltimas lneas de la salida. Mientras que la atmica
Versin muestra hilos con valores de incremento nicos, cada uno de los diez primeros hilos en el inseguro
Versin incrementada desde el mismo valor de cero y por lo tanto todos escriben el mismo valor, uno.
Tenga en cuenta que algunas adiciones en la versin insegura an estn completas porque la salida final no era una,
Por lo que algunos hilos escribieron con xito en la memoria global y se volvieron a leer sus valores.
Este ejemplo ilustra el compromiso extremo en el desempeo y la correccin que tendr que enfrentar al decidir
Donde las operaciones atmicas son una necesidad y donde los accesos inseguros son una opcin. Usted debe ser
Muy cuidadoso al hacer esta evaluacin; La decisin de utilizar accesos no seguros no es una
La prctica de la programacin, y slo debe hacerse si usted est seguro de la correccin se mantiene.