Quelle est la différence entre la création d'un objet tampon avec clCreateBuffer + CL_MEM_COPY_HOST_PTR vs + clCreateBuffer clEnqueueWriteBuffer?

StackOverflow https://stackoverflow.com/questions/3832963

  •  26-09-2019
  •  | 
  •  

Question

J'ai vu les deux versions dans des tutoriels, mais je ne pouvais pas savoir, quels sont leurs avantages et les inconvénients sont. Laquelle est la bonne?

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY,sizeof(float) * DATA_SIZE, NULL, NULL);
clEnqueueWriteBuffer(command_queue, input, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputdata, 0, NULL, NULL);

vs.

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ,sizeof(float) * DATA_SIZE, inputdata, NULL);

Merci.

[Mise à jour]

J'ajouté CL_MEM_COPY_HOST_PTR, au second exemple pour la rendre correcte.

Était-ce utile?

La solution

Je suppose que donnéesEntrée est NULL.

Dans ce cas, la deuxième approche ne devrait pas fonctionner du tout, étant donné que les spécifications dit, que clCreateBuffer renvoie NULL et une erreur, si:

  

CL_INVALID_HOST_PTR si host_ptr est NULL et CL_MEM_USE_HOST_PTR ou CL_MEM_COPY_HOST_PTR sont situés dans des drapeaux ou si host_ptr n'est pas NULL mais CL_MEM_COPY_HOST_PTR ou CL_MEM_USE_HOST_PTR ne sont pas définies dans les drapeaux.

donc vous dire soit

clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL);

ou

clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL);

Le premier devrait être plus ou moins la même chose que la première approche que vous avez montré, tandis que le second ne copie pas réellement les données, mais plutôt utiliser l'emplacement de mémoire fournie pour le stockage de la mémoire tampon (parties mise en cache ou tout en mémoire de l'appareil). Laquelle de ces deux est mieux dépend du scénario d'utilisation évidemment.

Personnellement, je préfère utiliser l'approche en deux étapes de la première allocation du tampon et remplir ensuite avec un writeToBuffer, depuis que je trouve plus facile de voir ce qui se passe (bien sûr une étape pourrait être plus rapide (ou il ne pourrait pas, c'est juste un estimation))

Autres conseils

Au cours de mon travail avec OpenCL j'ai trouvé une différence très importante entre

cl_mem CT = clCreateImage3DContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR , Volume_format, X, Y, Z, rowPitch, slicePitch, sourceData, &error);

et

cl_mem CT = clCreateImage3D(Context, CL_MEM_READ_ONLY , Volume_format, X, Y, Z, 0, 0, 0, &error);
error = clEnqueueWriteImage(CommandQue, CT, CL_TRUE, origin, region, rowPitch, slicePitch, sourceData, 0, 0, 0);

Pour la première approche OpenCL copiera le pointeur hôte non directement au GPU. D'abord, il attribuera un second tampon temporaire sur l'hôte qui peut causer des problèmes si vous chargez gros trucs comme un CT au GPU. Pour un court laps de temps la mémoire nécessaire est la taille de CT deux fois. Aussi les données ne sont pas copiées au cours de cette fonction. Elle est copiée lors de la mise en argument à la fonction du noyau qui utilise l'objet d'image 3D.

La seconde approche copie directe des données au GPU. Il n'y a pas d'allocations supplémentaires effectuées par OpenCL. Je pense que cela est probablement le même pour les objets tampons normaux.

L'aspect agréable de la première approche, est que « clEnqueueWriteBuffer » vous permet d'assigner un événement à la copie d'un tampon. Alors, disons que vous voulez mesurer le temps qu'il faut pour copier des données sur le GPU en utilisant les options de GPU_Profiling, vous serez en mesure de le faire avec la première approche, mais pas le second.

La seconde approche est plus compact, plus facile à lire, et nécessite moins de lignes de code.

Une différence majeure que j'ai rencontré:

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY,sizeof(float) * DATA_SIZE, NULL, NULL); clEnqueueWriteBuffer(command_queue, input, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputdata, 0, NULL, NULL);

Ce premier ensemble de commandes créera un tampon vide et une commande en file d'attente dans la file d'attente de commandes pour remplir la mémoire tampon.

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ,sizeof(float) * DATA_SIZE, inputdata, NULL)

Cette deuxième commande va créer le tampon et le remplir immédiatement. Notez qu'il n'y a pas de file d'attente de commande dans cette liste d'arguments, il utilise le contenu des données d'entrée comme il est en ce moment.

Si vous avez déjà été en cours d'exécution du code CL et votre pointeur source dépend d'une commande précédente dans la file d'attente de commande terminé (par exemple une en file d'attente de lecture d'un tampon de sortie avant), vous voulez certainement utiliser la 1ère méthode. Si vous essayez de créer et remplir la mémoire tampon en une seule commande, vous vous retrouverez avec une condition de course dans laquelle le contenu du tampon ne sera pas correctement attendre la fin de votre lecture tampon avant.

Eh bien la principale différence entre ces deux est que le premier alloue de la mémoire sur l'appareil, puis copie des données à cette mémoire. Le deuxième seulement Alloue.

Ou avez-vous clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL); moyen?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top