Je cherche à obtenir un nombre aléatoire dans Opencl. Il n'est pas nécessaire d'être réel aléatoire ou même si aléatoire. Juste quelque chose de simple et rapide.
Je vois qu'il y a une tonne d'algorithmes aléatoires aléatoires aléatoires de pantalons de fantaisie parallèle aléatoires dans Opencl qui ressemblent à des milliers de lignes. Je n'ai besoin de rien comme ça. Un simple 'aléatoire ()' serait bien, même s'il est facile de voir des modèles dedans.
Je vois qu'il y a une fonction de bruit? Un moyen facile d'utiliser cela pour obtenir un nombre aléatoire?
Je résolvais ce problème "pas de hasard" depuis quelques jours et j'ai proposé trois approches différentes:
Xorshift - J'ai créé le générateur basé sur celui-ci. Tout ce que vous avez à faire est de fournir un numéro uint2
(Graine) pour le noyau entier et chaque élément de travail calculera son propre numéro de Rand.
// 'randoms' is uint2 passed to kernel
uint seed = randoms.x + globalID;
uint t = seed ^ (seed << 11);
uint result = randoms.y ^ (randoms.y >> 19) ^ (t ^ (t >> 8));
Java aléatoire - J'ai utilisé le code de .next(int bits)
méthode pour générer un nombre aléatoire. Cette fois, vous devez fournir un numéro ulong
sous forme de semences.
// 'randoms' is ulong passed to kernel
ulong seed = randoms + globalID;
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
uint result = seed >> 16;
Il suffit de générer tout sur la CPU et de le transmettre au noyau dans un grand tampon.
J'ai testé les trois approches (générateurs) dans mon algorithme d'évolution informatique indiquée minimale définie dans des graphiques.
J'aime les chiffres générés à partir du premier, mais cela ressemble à mon algorithme d'évolution.
Le deuxième générateur génère des nombres qui a un motif visible, mais mon algorithme d'évolution aime-être de cette façon de toute façon et une chose entière fonctionne peu plus vite qu'avec le premier générateur.
Mais la troisième approche montre qu'il est absolument d'accord pour offrir à tous les chiffres de l'hôte (CPU). D'abord, je pense que cela générant (dans mon cas) 1536 chiffres et les transmettre à GPU dans chaque appel de noyau serait trop coûteux (pour calculer et transférer au GPU). Mais il s'avère, c'est aussi vite que mes tentatives précédentes. Et la charge de la CPU reste moins de 5%.
BTW, j'ai aussi essayé mwc64x aléatoire mais après avoir installé un nouveau pilote GPU, la fonction mul_hi
Commence à provoquer l'échec de la construction (même un analyser de noyau AMD entièrement s'est écrasé).
Je suis en train de mettre en œuvre un traceur de chemin en temps réel. Vous savez peut-être déjà que le traçage du chemin nécessite de nombreux nombres aléatoires.
[.____] Avant de générer des nombres aléatoires sur le GPU, je les ai tout simplement générés sur la CPU (en utilisant Rand (), qui craint) et les a transmis au GPU.
[.____] qui est rapidement devenu un goulot d'étranglement.
Maintenant, je génère les nombres aléatoires sur le GPU avec le générateur de numéros de Pseudorandom Park-Miller (PRNG).
[.____] Il est extrêmement simple de mettre en œuvre et réalise de très bons résultats.
[.____] J'ai pris des milliers d'échantillons (dans la gamme de 0,0 à 1,0) et les a compris.
[.____] La valeur résultante était très proche de 0,5 (ce que vous attendez). Entre différentes pistes, la divergence de 0,5 était d'environ 0,002. Par conséquent, il a une distribution très uniforme.
[.____] Voici un article décrivant l'algorithme:
[.____] http://www.cems.uwe.ac.uk/~irjohnso/coursenotes/ufeen8-15-m/p1192-parkmiller.pdf
Et voici un papier sur l'algorithme ci-dessus optimisé pour Cuda (qui peut facilement être porté à OpenCl): http://www0.cs.ucl.ac.uk/staff/ucacbbl/ftp/ papiers/langdon_2009_cigpu.pdf
[.____] Voici un exemple de la façon dont j'utilise:
int Rand(int* seed) // 1 <= *seed < m
{
int const a = 16807; //ie 7**5
int const m = 2147483647; //ie 2**31-1
*seed = (long(*seed * a))%m;
return(*seed);
}
kernel random_number_kernel(global int* seed_memory)
{
int global_id = get_global_id(1) * get_global_size(0) + get_global_id(0); // Get the global id in 1D.
// Since the Park-Miller PRNG generates a SEQUENCE of random numbers
// we have to keep track of the previous random number, because the next
// random number will be generated using the previous one.
int seed = seed_memory[global_id];
int random_number = Rand(&seed); // Generate the next random number in the sequence.
seed_memory[global_id] = *seed; // Save the seed for the next time this kernel gets enqueued.
}
Le code sert tout comme un exemple. Je ne l'ai pas testé.
[.____] Le tableau "Green_Memory" est rempli de rand () seulement une fois avant la première exécution du noyau. Après cela, toute génération de nombres aléatoires se produit sur le GPU. Je pense qu'il est également possible de simplement utiliser l'ID du noyau au lieu d'initialiser le tableau avec Rand ().
Il semble que Opencl ne fournit pas de telles fonctionnalités. Cependant, certaines personnes ont fait des recherches à ce sujet et fournissent un code sous licence BSD pour produire de bons numéros aléatoires sur le GPU.
Ceci est ma version de l'opencl float Pseudorandom bruit, à l'aide de la fonction trigonométrique
//noise values in range if 0.0 to 1.0
static float noise3D(float x, float y, float z) {
float ptr = 0.0f;
return fract(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f, &ptr);
}
__kernel void fillRandom(float seed, __global float* buffer, int length) {
int gi = get_global_id(0);
float fgi = float(gi)/length;
buffer[gi] = noise3D(fgi, 0.0f, seed);
}
Vous pouvez générer 1D ou 2D NOADY en passant à bruit3d index de coordonnées d'index normalisées sous forme de premiers paramètres et la graine aléatoire (générée sur la CPU par exemple) en tant que dernier paramètre.
Voici quelques photos de bruit générées avec ce noyau et différentes graines:
J'ai eu le même problème. www.thesalmons.org/john/random123/papers/random123sc11.pdf
Vous pouvez trouver la documentation ici. http://www.thesalys.org/john/random123/relases/latest/docs/index.html
Vous pouvez télécharger la bibliothèque ici: http://www.dawawresearch.com/resources_random123.html
Le GPU n'a pas de bonnes sources de hasard, mais cela peut être facilement surmonté en semant un noyau avec une graine aléatoire de l'hôte. Après cela, vous avez juste besoin d'un algorithme pouvant fonctionner avec un nombre massif de fils simultanés.
Ce lien décrit une implémentation Twister Mersenne à l'aide de OPENCL: Twister parallèle Mersenne Twister . Vous pouvez également trouver une implémentation dans le SDK NVIDIA.