Présentation de C++ AMP
Remarque
Les en-têtes AMP C++ sont déconseillés à partir de Visual Studio 2022 version 17.0.
L’inclusion d’en-têtes AMP génère des erreurs de génération. Définissez _SILENCE_AMP_DEPRECATION_WARNINGS
avant d’inclure tous les en-têtes AMP pour silence les avertissements.
Le parallélisme massif accéléré C++ (C++ AMP) accélère l’exécution du code C++ en tirant parti du matériel parallèle aux données, tel qu’une unité de traitement graphique (GPU) sur une carte graphique discrète. En utilisant C++ AMP, vous pouvez coder des algorithmes de données multidimensionnels afin que l’exécution puisse être accélérée à l’aide du parallélisme sur du matériel hétérogène. Le modèle de programmation C++ AMP comprend des tableaux multidimensionnels, l’indexation, le transfert mémoire, la mosaïque et une bibliothèque de fonctions mathématiques. Vous pouvez utiliser des extensions de langage AMP C++ pour contrôler la façon dont les données sont déplacées de l’UC vers le GPU et de retour, afin d’améliorer les performances.
Configuration requise
Windows 7 ou version ultérieure
Windows Server 2008 R2 à Visual Studio 2019.
Matériel directX 11.0 ou version ultérieure
Pour le débogage sur l’émulateur logiciel, Windows 8 ou Windows Server 2012 est requis. Pour effectuer un débogage sur le matériel, vous devez installer les pilotes de votre carte graphique. Pour plus d’informations, consultez Débogage du code GPU.
Remarque : AMP n’est actuellement pas pris en charge sur ARM64.
Introduction
Les deux exemples suivants illustrent les principaux composants de L’AMP C++. Supposons que vous souhaitez ajouter les éléments correspondants de deux tableaux unidimensionnels. Par exemple, vous souhaiterez peut-être ajouter {1, 2, 3, 4, 5}
et {6, 7, 8, 9, 10}
obtenir {7, 9, 11, 13, 15}
. Sans utiliser C++ AMP, vous pouvez écrire le code suivant pour ajouter les nombres et afficher les résultats.
#include <iostream>
void StandardMethod() {
int aCPP[] = {1, 2, 3, 4, 5};
int bCPP[] = {6, 7, 8, 9, 10};
int sumCPP[5];
for (int idx = 0; idx < 5; idx++)
{
sumCPP[idx] = aCPP[idx] + bCPP[idx];
}
for (int idx = 0; idx < 5; idx++)
{
std::cout << sumCPP[idx] << "\n";
}
}
Les parties importantes du code sont les suivantes :
Données : les données se composent de trois tableaux. Tous ont le même rang (un) et la longueur (cinq).
Itération : la première
for
boucle fournit un mécanisme permettant d’itérer au sein des éléments des tableaux. Le code que vous souhaitez exécuter pour calculer les sommes est contenu dans le premierfor
bloc.Index : la
idx
variable accède aux éléments individuels des tableaux.
À l’aide de C++ AMP, vous pouvez écrire le code suivant à la place.
#include <amp.h>
#include <iostream>
using namespace concurrency;
const int size = 5;
void CppAmpMethod() {
int aCPP[] = {1, 2, 3, 4, 5};
int bCPP[] = {6, 7, 8, 9, 10};
int sumCPP[size];
// Create C++ AMP objects.
array_view<const int, 1> a(size, aCPP);
array_view<const int, 1> b(size, bCPP);
array_view<int, 1> sum(size, sumCPP);
sum.discard_data();
parallel_for_each(
// Define the compute domain, which is the set of threads that are created.
sum.extent,
// Define the code to run on each thread on the accelerator.
[=](index<1> idx) restrict(amp) {
sum[idx] = a[idx] + b[idx];
}
);
// Print the results. The expected output is "7, 9, 11, 13, 15".
for (int i = 0; i < size; i++) {
std::cout << sum[i] << "\n";
}
}
Les mêmes éléments de base sont présents, mais les constructions AMP C++ sont utilisées :
Données : vous utilisez des tableaux C++ pour construire trois objets AMP C++ array_view . Vous fournissez quatre valeurs pour construire un
array_view
objet : les valeurs de données, le classement, le type d’élément et la longueur de l’objetarray_view
dans chaque dimension. Le classement et le type sont passés en tant que paramètres de type. Les données et la longueur sont passées en tant que paramètres de constructeur. Dans cet exemple, le tableau C++ passé au constructeur est unidimensionnel. Le rang et la longueur sont utilisés pour construire la forme rectangulaire des données dans l’objetarray_view
, et les valeurs de données sont utilisées pour remplir le tableau. La bibliothèque runtime inclut également la classe de tableau, qui a une interface qui ressemble à laarray_view
classe et qui est décrite plus loin dans cet article.Itération : la fonction parallel_for_each (C++ AMP) fournit un mécanisme d’itération via les éléments de données ou le domaine de calcul. Dans cet exemple, le domaine de calcul est spécifié par
sum.extent
. Le code que vous souhaitez exécuter est contenu dans une expression lambda ou dans une fonction de noyau. Indiquerestrict(amp)
que seul le sous-ensemble du langage C++ que C++ AMP peut accélérer est utilisé.Index : la variable de classe d’index, est
idx
déclarée avec un rang d’un pour correspondre au rang de l’objetarray_view
. À l’aide de l’index, vous pouvez accéder aux éléments individuels desarray_view
objets.
Mise en forme et indexation des données : index et étendue
Vous devez définir les valeurs de données et déclarer la forme des données avant de pouvoir exécuter le code du noyau. Toutes les données sont définies comme un tableau (rectangulaire) et vous pouvez définir le tableau pour avoir n’importe quel rang (nombre de dimensions). Les données peuvent être de n’importe quelle taille dans l’une des dimensions.
index, classe
La classe d’index spécifie un emplacement dans le ou array_view
l’objet array
encapsulant le décalage de l’origine dans chaque dimension en un seul objet. Lorsque vous accédez à un emplacement dans le tableau, vous passez un index
objet à l’opérateur d’indexation, []
au lieu d’une liste d’index entiers. Vous pouvez accéder aux éléments de chaque dimension à l’aide de l’opérateur array ::operator() ou de l’opérateur array_view ::operator().
L’exemple suivant crée un index unidimensionnel qui spécifie le troisième élément d’un objet unidimensionnel array_view
. L’index est utilisé pour imprimer le troisième élément de l’objet array_view
. La sortie est 3.
int aCPP[] = {1, 2, 3, 4, 5};
array_view<int, 1> a(5, aCPP);
index<1> idx(2);
std::cout << a[idx] << "\n";
// Output: 3
L’exemple suivant crée un index à deux dimensions qui spécifie l’élément où la ligne = 1 et la colonne = 2 dans un objet à deux dimensions array_view
. Le premier paramètre du index
constructeur est le composant de ligne, et le deuxième paramètre est le composant de colonne. La sortie est 6.
int aCPP[] = {1, 2, 3, 4, 5, 6};
array_view<int, 2> a(2, 3, aCPP);
index<2> idx(1, 2);
std::cout <<a[idx] << "\n";
// Output: 6
L’exemple suivant crée un index tridimensionnel qui spécifie l’élément où la profondeur = 0, la ligne = 1 et la colonne = 3 dans un objet tridimensionnel array_view
. Notez que le premier paramètre est le composant de profondeur, le deuxième paramètre est le composant de ligne, et le troisième paramètre est le composant de colonne. La sortie est 8.
int aCPP[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
array_view<int, 3> a(2, 3, 4, aCPP);
// Specifies the element at 3, 1, 0.
index<3> idx(0, 1, 3);
std::cout << a[idx] << "\n";
// Output: 8
extent, classe
La classe d’étendue spécifie la longueur des données dans chaque dimension de l’objet ou array_view
de l’objetarray
. Vous pouvez créer une extension et l’utiliser pour créer un ou array_view
un array
objet. Vous pouvez également récupérer l’étendue d’un objet ou array_view
d’un objet existantarray
. L’exemple suivant imprime la longueur de l’étendue dans chaque dimension d’un array_view
objet.
int aCPP[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// There are 3 rows and 4 columns, and the depth is two.
array_view<int, 3> a(2, 3, 4, aCPP);
std::cout << "The number of columns is " << a.extent[2] << "\n";
std::cout << "The number of rows is " << a.extent[1] << "\n";
std::cout << "The depth is " << a.extent[0] << "\n";
std::cout << "Length in most significant dimension is " << a.extent[0] << "\n";
L’exemple suivant crée un array_view
objet qui a les mêmes dimensions que l’objet dans l’exemple précédent, mais cet exemple utilise un extent
objet au lieu d’utiliser des paramètres explicites dans le array_view
constructeur.
int aCPP[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
extent<3> e(2, 3, 4);
array_view<int, 3> a(e, aCPP);
std::cout << "The number of columns is " << a.extent[2] << "\n";
std::cout << "The number of rows is " << a.extent[1] << "\n";
std::cout << "The depth is " << a.extent[0] << "\n";
Déplacement de données vers l’accélérateur : tableau et array_view
Deux conteneurs de données utilisés pour déplacer des données vers l’accélérateur sont définis dans la bibliothèque runtime. Il s’agit de la classe de tableau et de la classe array_view. La array
classe est une classe de conteneur qui crée une copie approfondie des données lorsque l’objet est construit. La array_view
classe est une classe wrapper qui copie les données lorsque la fonction noyau accède aux données. Lorsque les données sont nécessaires sur l’appareil source, les données sont copiées.
array, classe
Lorsqu’un array
objet est construit, une copie approfondie des données est créée sur l’accélérateur si vous utilisez un constructeur qui inclut un pointeur vers le jeu de données. La fonction noyau modifie la copie sur l’accélérateur. Une fois l’exécution de la fonction de noyau terminée, vous devez recopier les données dans la structure de données source. L’exemple suivant multiplie chaque élément d’un vecteur de 10. Une fois la fonction de noyau terminée, elle vector conversion operator
est utilisée pour copier les données dans l’objet vector.
std::vector<int> data(5);
for (int count = 0; count <5; count++)
{
data[count] = count;
}
array<int, 1> a(5, data.begin(), data.end());
parallel_for_each(
a.extent,
[=, &a](index<1> idx) restrict(amp) {
a[idx] = a[idx]* 10;
});
data = a;
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n";
}
array_view, classe
Il array_view
a presque les mêmes membres que la array
classe, mais le comportement sous-jacent n’est pas le même. Les données transmises au array_view
constructeur ne sont pas répliquées sur le GPU, car il s’agit d’un array
constructeur. Au lieu de cela, les données sont copiées dans l’accélérateur lorsque la fonction du noyau est exécutée. Par conséquent, si vous créez deux array_view
objets qui utilisent les mêmes données, les deux array_view
objets font référence au même espace mémoire. Lorsque vous effectuez cette opération, vous devez synchroniser n’importe quel accès multithread. L’avantage principal de l’utilisation de la classe est que les array_view
données sont déplacées uniquement si nécessaire.
Comparaison de tableaux et de array_view
Le tableau suivant récapitule les similitudes et les différences entre les classes et array_view
les array
classes.
Description | array (classe) | array_view (classe) |
---|---|---|
Lorsque le classement est déterminé | Au moment de la compilation. | Au moment de la compilation. |
Quand l’étendue est déterminée | Au moment de l’exécution. | Au moment de l’exécution. |
Forme | Rectangulaire. | Rectangulaire. |
Stockage des données | Conteneur de données. | Est un wrapper de données. |
Copier | Copie explicite et approfondie au niveau de la définition. | Copie implicite lorsqu’elle est accessible par la fonction noyau. |
Récupération de données | En copiant les données de tableau vers un objet sur le thread d’UC. | En accédant directement à l’objet array_view ou en appelant la méthode array_view ::synchronize pour continuer à accéder aux données sur le conteneur d’origine. |
Mémoire partagée avec tableau et array_view
La mémoire partagée est la mémoire accessible à la fois par le processeur et l’accélérateur. L’utilisation de la mémoire partagée élimine ou réduit considérablement la surcharge liée à la copie de données entre l’UC et l’accélérateur. Bien que la mémoire soit partagée, elle ne peut pas être accessible simultanément par l’UC et l’accélérateur, et cela provoque un comportement non défini.
array
les objets peuvent être utilisés pour spécifier un contrôle précis sur l’utilisation de la mémoire partagée si l’accélérateur associé le prend en charge. Si un accélérateur prend en charge la mémoire partagée est déterminé par la propriété supports_cpu_shared_memory de l’accélérateur, qui retourne true
lorsque la mémoire partagée est prise en charge. Si la mémoire partagée est prise en charge, l’énumération par défaut access_type pour les allocations de mémoire sur l’accélérateur est déterminée par la default_cpu_access_type
propriété. Par défaut, array
et array_view
les objets prennent le même access_type
rôle que le principal associé accelerator
.
En définissant le tableau ::cpu_access_type propriété Membre de données d’un array
point de vue explicite, vous pouvez exercer un contrôle précis sur l’utilisation de la mémoire partagée, afin que vous puissiez optimiser l’application pour les caractéristiques de performances du matériel, en fonction des modèles d’accès à la mémoire de ses noyaux de calcul. Il array_view
reflète la même cpu_access_type
chose que celui array
auquel il est associé ; ou, si le array_view est construit sans source de données, il access_type
reflète l’environnement qui l’amène d’abord à allouer du stockage. Autrement dit, s’il est d’abord accédé par l’hôte (UC), il se comporte comme s’il a été créé sur une source de données de l’UC et partage l’élément access_type
accelerator_view
associé par capture ; toutefois, s’il est d’abord accédé par un accelerator_view
, il se comporte comme s’il a été créé sur un array
objet créé sur celui-ci accelerator_view
et partage les array
access_type
données .
L’exemple de code suivant montre comment déterminer si l’accélérateur par défaut prend en charge la mémoire partagée, puis crée plusieurs tableaux qui ont des configurations cpu_access_type différentes.
#include <amp.h>
#include <iostream>
using namespace Concurrency;
int main()
{
accelerator acc = accelerator(accelerator::default_accelerator);
// Early out if the default accelerator doesn't support shared memory.
if (!acc.supports_cpu_shared_memory)
{
std::cout << "The default accelerator does not support shared memory" << std::endl;
return 1;
}
// Override the default CPU access type.
acc.default_cpu_access_type = access_type_read_write
// Create an accelerator_view from the default accelerator. The
// accelerator_view inherits its default_cpu_access_type from acc.
accelerator_view acc_v = acc.default_view;
// Create an extent object to size the arrays.
extent<1> ex(10);
// Input array that can be written on the CPU.
array<int, 1> arr_w(ex, acc_v, access_type_write);
// Output array that can be read on the CPU.
array<int, 1> arr_r(ex, acc_v, access_type_read);
// Read-write array that can be both written to and read from on the CPU.
array<int, 1> arr_rw(ex, acc_v, access_type_read_write);
}
Exécution de code sur des données : parallel_for_each
La fonction parallel_for_each définit le code que vous souhaitez exécuter sur l’accélérateur sur les données de l’objet ou array_view
de l’objetarray
. Considérez le code suivant à partir de l’introduction de cette rubrique.
#include <amp.h>
#include <iostream>
using namespace concurrency;
void AddArrays() {
int aCPP[] = {1, 2, 3, 4, 5};
int bCPP[] = {6, 7, 8, 9, 10};
int sumCPP[5] = {0, 0, 0, 0, 0};
array_view<int, 1> a(5, aCPP);
array_view<int, 1> b(5, bCPP);
array_view<int, 1> sum(5, sumCPP);
parallel_for_each(
sum.extent,
[=](index<1> idx) restrict(amp)
{
sum[idx] = a[idx] + b[idx];
}
);
for (int i = 0; i < 5; i++) {
std::cout << sum[i] << "\n";
}
}
La parallel_for_each
méthode prend deux arguments, un domaine de calcul et une expression lambda.
Le domaine de calcul est un extent
objet ou un tiled_extent
objet qui définit l’ensemble de threads à créer pour l’exécution parallèle. Un thread est généré pour chaque élément du domaine de calcul. Dans ce cas, l’objet extent
est unidimensionnel et a cinq éléments. Par conséquent, cinq threads sont démarrés.
L’expression lambda définit le code à exécuter sur chaque thread. La clause de capture, spécifie que le corps de l’expression lambda accède à toutes les variables capturées par valeur, qui, dans ce cas, [=]
sont a
, b
et sum
. Dans cet exemple, la liste de paramètres crée une variable unidimensionnelle index
nommée idx
. La valeur de la idx[0]
valeur est 0 dans le premier thread et augmente d’une dans chaque thread suivant. Indique restrict(amp)
que seul le sous-ensemble du langage C++ que C++ AMP peut accélérer est utilisé. Les limitations relatives aux fonctions qui ont le modificateur de restriction sont décrites dans restrict (C++ AMP). Pour plus d’informations, consultez la syntaxe d’expression lambda.
L’expression lambda peut inclure le code à exécuter ou appeler une fonction de noyau distincte. La fonction noyau doit inclure le restrict(amp)
modificateur. L’exemple suivant équivaut à l’exemple précédent, mais il appelle une fonction de noyau distincte.
#include <amp.h>
#include <iostream>
using namespace concurrency;
void AddElements(
index<1> idx,
array_view<int, 1> sum,
array_view<int, 1> a,
array_view<int, 1> b) restrict(amp) {
sum[idx] = a[idx] + b[idx];
}
void AddArraysWithFunction() {
int aCPP[] = {1, 2, 3, 4, 5};
int bCPP[] = {6, 7, 8, 9, 10};
int sumCPP[5] = {0, 0, 0, 0, 0};
array_view<int, 1> a(5, aCPP);
array_view<int, 1> b(5, bCPP);
array_view<int, 1> sum(5, sumCPP);
parallel_for_each(
sum.extent,
[=](index<1> idx) restrict(amp) {
AddElements(idx, sum, a, b);
}
);
for (int i = 0; i < 5; i++) {
std::cout << sum[i] << "\n";
}
}
Accélération du code : vignettes et barrières
Vous pouvez bénéficier d’une accélération supplémentaire à l’aide du tiling. Le mosaïque divise les threads en sous-ensembles ou vignettes rectangulaires égaux. Vous déterminez la taille de vignette appropriée en fonction de votre jeu de données et de l’algorithme que vous codez. Pour chaque thread, vous avez accès à l’emplacement global d’un élément de données par rapport à l’ensemble array
ou array_view
à l’accès à l’emplacement local par rapport à la vignette. L’utilisation de la valeur d’index locale simplifie votre code, car vous n’avez pas besoin d’écrire le code pour traduire les valeurs d’index de global en local. Pour utiliser le mosaïque, appelez la méthode extent ::tile sur le domaine de calcul de la parallel_for_each
méthode et utilisez un objet tiled_index dans l’expression lambda.
Dans les applications classiques, les éléments d’une vignette sont liés d’une certaine manière, et le code doit accéder à la vignette et effectuer le suivi des valeurs dans la vignette. Utilisez le mot clé tile_static Keyword et la méthode tile_barrier ::wait pour effectuer cette opération. Une variable qui a le mot clé tile_static a une étendue sur une vignette entière et une instance de la variable est créée pour chaque vignette. Vous devez gérer la synchronisation de l’accès aux threads de vignettes à la variable. La méthode tile_barrier :wait arrête l’exécution du thread actuel jusqu’à ce que tous les threads de la vignette aient atteint l’appel .tile_barrier::wait
Vous pouvez donc accumuler des valeurs sur la vignette à l’aide de variables tile_static. Vous pouvez ensuite terminer tous les calculs qui nécessitent l’accès à toutes les valeurs.
Le diagramme suivant représente un tableau bidimensionnel de données d’échantillonnage organisées en vignettes.
L’exemple de code suivant utilise les données d’échantillonnage du diagramme précédent. Le code remplace chaque valeur de la vignette par la moyenne des valeurs de la vignette.
// Sample data:
int sampledata[] = {
2, 2, 9, 7, 1, 4,
4, 4, 8, 8, 3, 4,
1, 5, 1, 2, 5, 2,
6, 8, 3, 2, 7, 2};
// The tiles:
// 2 2 9 7 1 4
// 4 4 8 8 3 4
//
// 1 5 1 2 5 2
// 6 8 3 2 7 2
// Averages:
int averagedata[] = {
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
};
array_view<int, 2> sample(4, 6, sampledata);
array_view<int, 2> average(4, 6, averagedata);
parallel_for_each(
// Create threads for sample.extent and divide the extent into 2 x 2 tiles.
sample.extent.tile<2,2>(),
[=](tiled_index<2,2> idx) restrict(amp) {
// Create a 2 x 2 array to hold the values in this tile.
tile_static int nums[2][2];
// Copy the values for the tile into the 2 x 2 array.
nums[idx.local[1]][idx.local[0]] = sample[idx.global];
// When all the threads have executed and the 2 x 2 array is complete, find the average.
idx.barrier.wait();
int sum = nums[0][0] + nums[0][1] + nums[1][0] + nums[1][1];
// Copy the average into the array_view.
average[idx.global] = sum / 4;
});
for (int i = 0; i <4; i++) {
for (int j = 0; j <6; j++) {
std::cout << average(i,j) << " ";
}
std::cout << "\n";
}
// Output:
// 3 3 8 8 3 3
// 3 3 8 8 3 3
// 5 5 2 2 4 4
// 5 5 2 2 4 4
Bibliothèques mathématiques
L’AMP C++ comprend deux bibliothèques mathématiques. La bibliothèque double précision de l’espace de noms Concurrency ::p recise_math prend en charge les fonctions double précision. Il fournit également la prise en charge des fonctions à précision unique, bien que la prise en charge double précision sur le matériel soit toujours nécessaire. Il est conforme à la spécification C99 (ISO/IEC 9899). L’accélérateur doit prendre en charge la double précision complète. Vous pouvez déterminer s’il le fait en vérifiant la valeur de l’accélérateur ::supports_double_precision Membre de données. La bibliothèque mathématique rapide, dans l’espace de noms Concurrency ::fast_math, contient un autre ensemble de fonctions mathématiques. Ces fonctions, qui prennent en charge uniquement float
les opérandes, s’exécutent plus rapidement, mais ne sont pas aussi précises que celles de la bibliothèque mathématique double précision. Les fonctions sont contenues dans le <fichier d’en-tête amp_math.h> et toutes sont déclarées avec restrict(amp)
. Les fonctions du fichier d’en-tête <cmath> sont importées dans les espaces de noms et precise_math
les fast_math
espaces de noms. Le restrict
mot clé est utilisé pour distinguer la <version cmath> et la version AMP C++. Le code suivant calcule le logarithme de base 10, à l’aide de la méthode rapide, de chaque valeur qui se trouve dans le domaine de calcul.
#include <amp.h>
#include <amp_math.h>
#include <iostream>
using namespace concurrency;
void MathExample() {
double numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 1000.0 };
array_view<double, 1> logs(6, numbers);
parallel_for_each(
logs.extent,
[=] (index<1> idx) restrict(amp) {
logs[idx] = concurrency::fast_math::log10(numbers[idx]);
}
);
for (int i = 0; i < 6; i++) {
std::cout << logs[i] << "\n";
}
}
Bibliothèque graphique
C++ AMP inclut une bibliothèque graphique conçue pour la programmation graphique accélérée. Cette bibliothèque est utilisée uniquement sur les appareils qui prennent en charge les fonctionnalités graphiques natives. Les méthodes se trouvent dans l’espace de noms Concurrency ::graphics et sont contenues dans le <fichier d’en-tête amp_graphics.h> . Les composants clés de la bibliothèque graphique sont les suivants :
classe de texture : vous pouvez utiliser la classe de texture pour créer des textures à partir de la mémoire ou d’un fichier. Les textures ressemblent à des tableaux, car elles contiennent des données et ressemblent à des conteneurs dans la bibliothèque standard C++ en ce qui concerne l’affectation et la construction de copie. Pour plus d’informations, consultez Conteneurs disponibles dans la bibliothèque C++ Standard. Les paramètres de modèle de la
texture
classe sont le type d’élément et le classement. Le rang peut être de 1, 2 ou 3. Le type d’élément peut être l’un des types de vecteurs courts décrits plus loin dans cet article.classe writeonly_texture_view : fournit un accès en écriture seule à n’importe quelle texture.
Bibliothèque de vecteurs courts : définit un ensemble de types de vecteurs courts de longueur 2, 3 et 4 basés sur
int
, ,float
uint
,double
, norme ou unorm.
Applications de la plateforme Windows universelle (UWP)
Comme d’autres bibliothèques C++, vous pouvez utiliser C++ AMP dans vos applications UWP. Ces articles décrivent comment inclure du code AMP C++ dans les applications créées à l’aide de C++, C#, Visual Basic ou JavaScript :
Optimiseur de voyage Bing Maps, application Windows Store en JavaScript et C++
Comment utiliser C++ AMP à partir de C# à l’aide de Windows Runtime
Visualiseur AMP et concurrence C++
Le visualiseur concurrentiel inclut la prise en charge de l’analyse des performances du code AMP C++. Ces articles décrivent ces fonctionnalités :
Suggestions sur les niveaux de performance
Les modulus et la division d’entiers non signés ont beaucoup mieux de performances que les modulus et la division des entiers signés. Nous vous recommandons d’utiliser des entiers non signés lorsque cela est possible.
Voir aussi
C++ AMP (C++ Accelerated Massive Parallelism)
Syntaxe d’expression lambda
Référence (C++ AMP)
Programmation parallèle dans le blog du code natif