summaryrefslogtreecommitdiff
path: root/random.html
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2020-09-23 11:58:39 +0200
committerTom Smeding <tom.smeding@gmail.com>2020-09-23 11:58:39 +0200
commit20fdc927770e478685ddb502296259df46513a69 (patch)
tree07228155849652192f34b250275d66ecd49c2a04 /random.html
parent3a0517330bcfb8045210cf3922d920803d6682b2 (diff)
Add random post
Diffstat (limited to 'random.html')
-rw-r--r--random.html59
1 files changed, 59 insertions, 0 deletions
diff --git a/random.html b/random.html
new file mode 100644
index 0000000..741f4e3
--- /dev/null
+++ b/random.html
@@ -0,0 +1,59 @@
+<h3>Generating randomness on a GPU</h3>
+
+<p>
+ When doing simulations on a GPU, e.g. for raytracing, one very quickly needs lots and lots of random numbers.
+ There is a huge number of fine random number generators, but on a GPU you often have a very particular problem: you need lots of random numbers in a large array, generated in parallel.
+ This is not something that conventional PRNG's like the <a href="https://en.wikipedia.org/wiki/Xorshift" target="_blank">xorshift family</a> are suitable for, since these are inherently sequential: to generate the next random number, it needs the state produced when generating the previous one.
+</p>
+
+<p>
+ You may try to solve this by initialising the RNG many times, each time with a different seed.
+ However, RNG's are not built for this: the random sequences generated for all those seeds will be different, probably, but they will still be quite correlated.
+ This is because the random generators are normally constructed to go <b>deep</b>, not <b>wide</b>.
+</p>
+
+<p>
+ This is an insight I learned from reading a post on Nathan Reed's blog, which has now unfortunately disappeared.
+ (In fact, its disappearance is the very reason for writing this post.)
+ However, there is <a href="https://web.archive.org/web/20200611022708/http://www.reedbeta.com/blog/quick-and-easy-gpu-random-numbers-in-d3d11/" target="_blank">a copy on the Internet Archive here</a>!
+ He astutely realises that RNG's are made to go <i>deep</i>, meaning that the random sequence generated for any particular seed will have good properties when seen on its own, and not to go <i>wide</i>: sequence values at similar depth for different seeds are not very independent.
+ However, <b>hash functions</b> are made to go <i>wide</i>: the outputs for different "seeds" are meant to be competely different and independent, while less emphasis is sometimes laid on the quality of the behaviour when going deep (i.e. when iterating the hash).
+</p>
+
+<p>
+ This means that when you need many random values in an array, in parallel, what you should do is take a hash function and apply it to the integers 1..n for whatever n you need.
+ The results will be nice.
+ Then, since hash functions are generally more expensive to compute than PRNG's, if you want more of those arrays, iterate mapping the PRNG over that array.
+</p>
+
+<p>
+ Nathan further recommends the following hash function, which is not a cryptographic hash function but is nothing short of magical when applied for the right purpose: the Wang hash.
+</p>
+
+<pre>uint wang_hash(uint seed) {
+ // taken from Nathan Reed's blog post
+ seed = (seed ^ 61) ^ (seed &gt;&gt; 16);
+ seed *= 9;
+ seed = seed ^ (seed &gt;&gt; 4);
+ seed *= 0x27d4eb2d;
+ seed = seed ^ (seed &gt;&gt; 15);
+ return seed;
+}</pre>
+
+<p>
+ This is a small, fast hash function that works well when applied in the above scheme.
+ As a PRNG, he suggests a LCG or an Xorshift generator; I think it's safest to use Xorshift.
+ An example implementation is here:
+</p>
+
+<pre>uint rand_xorshift() {
+ // Taken from Nathan Reed's blog post; originally from Marsaglia
+ rng_state ^= (rng_state &lt;&lt; 13);
+ rng_state ^= (rng_state &gt;&gt; 17);
+ rng_state ^= (rng_state &lt;&lt; 5);
+ return rng_state;
+}</pre>
+
+<p>
+ For neat images and a visual indication that this setup does generate nice random numbers, see Nathan's post linked above.
+</p>