{"id":9,"date":"2020-06-11T21:32:03","date_gmt":"2020-06-11T20:32:03","guid":{"rendered":"http:\/\/retrofocus.com\/?p=9"},"modified":"2021-01-09T22:29:47","modified_gmt":"2021-01-09T21:29:47","slug":"estimating-pi-using-the-monte-carlo-method-with-nvidia-gpu","status":"publish","type":"post","link":"http:\/\/retrofocus.com\/?p=9","title":{"rendered":"Estimating Pi using the Monte Carlo Method with Nvidia GPU"},"content":{"rendered":"\n<p>What does it feel to command 1280 parallel cores? Having earlier used GPU for machine learning with Keras, I wanted to have a look at directly programming the GPU with C\/C++. This is thus my first trial with GPU programming based on Nvidia CUDA framework. I&#8217;m using Nvidia 1060 GPU but I guess this would work on others as well. <\/p>\n\n\n\n<p>Here is my code: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include&lt;stdio.h>\n#include &lt;cuda.h>\n#include &lt;curand_kernel.h>\n__global__ void mc_pi(unsigned long long *countti,long iter,unsigned long long *palluu){\n        int ii;\n        curandState_t rng;\n        int tid = threadIdx.x + blockIdx.x * blockDim.x;\n        \/\/int blockid=blockIdx.x*blockDim.x;\n        countti&#91;tid]=0;\n        \/\/countti&#91;threadIdx.x]=0;\n        curand_init(clock64(), tid, 0, &amp;rng);\n        for(ii=0;ii&lt;iter;ii++){\n        float x = curand_uniform(&amp;rng); \n        float y = curand_uniform(&amp;rng); \n        if ((x*x+y*y) &lt;=1 )\n                {countti&#91;tid] += 1 ;    \n                }\n        }\n        __syncthreads();        \n        if (!(tid&amp;1)) {countti&#91;tid]=countti&#91;tid]+countti&#91;tid+1];}\n        __syncthreads();\n        if (!(tid&amp;3)) {countti&#91;tid]=countti&#91;tid]+countti&#91;tid+2];}\n        __syncthreads();\n        if (!(tid&amp;7)){countti&#91;tid]=countti&#91;tid]+countti&#91;tid+4];}\n        __syncthreads();\n        if (!(tid&amp;15)) {countti&#91;tid]=countti&#91;tid]+countti&#91;tid+8];}\n        __syncthreads();\n        if (!(tid&amp;31)) {countti&#91;tid]=countti&#91;tid]+countti&#91;tid+16];}\n        __syncthreads();\n        if (!(tid&amp;63)) {countti&#91;tid]=countti&#91;tid]+countti&#91;tid+32];}\n        __syncthreads();\n        if (!(tid&amp;127)) {countti&#91;tid]=countti&#91;tid]+countti&#91;tid+64];}\n        __syncthreads();\n        if (threadIdx.x ==0) {countti&#91;tid]=countti&#91;tid]+countti&#91;tid+128];\n        palluu&#91;blockIdx.x]=countti&#91;tid];}       \n        \/\/palluu&#91;blockIdx.x]=blockIdx.x;\n        __syncthreads();\n        \n}\n\n__global__ void reduce_blocks(unsigned long long *palluu){\nint tid = threadIdx.x;\/\/ + blockIdx.x * blockDim.x;\n\nint i=0;\nint oso= 0;\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif ((oso&amp;1) ==0 &amp;&amp; oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+1];}}\n__syncthreads();\n\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%4 ==0 &amp;&amp; oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+2];}}\n        __syncthreads();\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%8 ==0 &amp;&amp; oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+4];}}\n        __syncthreads();\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%16 ==0 &amp;&amp; oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+8];}}\n        __syncthreads();\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%32 ==0 &amp;&amp; oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+16];}}\n        __syncthreads();\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%64 ==0 &amp;&amp;oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+32];}}\n        __syncthreads();\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%128 ==0 &amp;&amp; oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+64];}}\n        __syncthreads();\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%256 ==0&amp;&amp;oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+128];}}\n        __syncthreads();\n\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%512 ==0&amp;&amp;oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+256];}}\n __syncthreads();\n\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%1024 ==0&amp;&amp;oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+512];}}\n __syncthreads();\n\n\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%2048 ==0&amp;&amp;oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+1024];}}\n __syncthreads();\n\nfor(i=0;i&lt;6;i++) {\noso=tid+(i*blockDim.x);\nif (oso%4096 ==0&amp;&amp;oso&lt;3906) {palluu&#91;oso]=palluu&#91;oso]+palluu&#91;oso+2048];}}\n __syncthreads();\n\n\n}\n\n\n\nint main() {\n        int N=999936;\n        int blocks=(N+255)\/256;\n        int threadsperblock=256;\n        int iterations=1000000;\n        int  i=0;\n        long total=0;\n        long long totalos=0;\n  unsigned long long *countti;\n  unsigned long long *resu;\n  unsigned long long *hostresu,hostresu2;\n        printf(\"blocks:%d, threadsperblock:%d\\n\",blocks,threadsperblock);\n  hostresu=(unsigned long long*)malloc(blocks*sizeof(long long));\n  cudaMalloc(&amp;countti, N*sizeof(unsigned long long));\n  cudaMalloc(&amp;resu,blocks * sizeof(unsigned long long));\n   mc_pi&lt;&lt;&lt;blocks,threadsperblock>>>(countti,iterations,resu); \n   \/\/reduce_blocks&lt;&lt;&lt;1,651>>>(resu);\n  reduce_blocks&lt;&lt;&lt;1,651>>>(resu);\n\n  cudaMemcpy((long long*)&amp;hostresu2,resu,sizeof(long long),cudaMemcpyDeviceToHost);\n  printf(\"Estimating Pi using the Monte Carlo Method in Nvidia 1060 parallel computation\\n: %.15f, based on %llu iterations\\n\",(double)hostresu2\/threadsperblock\/blocks\/iterations*4,threadsperblock*blocks*iterations);\n\nreturn 0;\n}\n<\/code><\/pre>\n\n\n\n<p>I&#8217;m using two kernel functions: mc_pi is used for actual pi monte carlo computations as well as for reducing the results on cuda block level. Second kernel reduce_blocks is used for reduction over 3906 blocks. The block size and number of blocks were quickly picked to come close to one million threads in total. <\/p>\n\n\n\n<p>Compilation, when you have Nvidia cuda framework installed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nvcc -o pi4cu pi4.cu <\/code><\/pre>\n\n\n\n<p>Performance: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>time .\/pi4cu \nblocks:3906, threadsperblock:256\nEstimating Pi using the Monte Carlo Method in Nvidia 1060 parallel computation\n: 3.141592344990079, based on 3503587328 iterations\n\nreal    0m27.299s\nuser    0m18.580s\nsys     0m8.704s\n<\/code><\/pre>\n\n\n\n<p>So 27 seconds it takes for 3.5 billion iterations maxing GPU to 100%. When compared again my single threaded python version of pi monte carlo function below, the performance improvement is 87x for GPU version. I&#8217;m almost sure this is still far from optimal. I read e.g. that GPU isn&#8217;t fast in the modulo operations in the reducers, thus in the mc_pi I already changed the modulos to bitwise operators but didn&#8217;t yet do it for the block level reducer. <\/p>\n\n\n\n<p>For reducers generally there would be different approaches to choose between. You can read more about those here: <a href=\"https:\/\/developer.download.nvidia.com\/assets\/cuda\/files\/reduction.pdf\">https:\/\/developer.download.nvidia.com\/assets\/cuda\/files\/reduction.pdf<\/a><\/p>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import random\nimport math\n\nsis=0\nlukum=40000000\n\nfor i in range(lukum):\n        a=random.random()\n        b=random.random()\n        if (a**2+b**2) &lt;= 1:\n                sis+=1\nprint(\"4*sis\/lukum:\", 4*sis\/lukum)\n<\/code><\/pre>\n\n\n\n<p>Accuracy? <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>My estimate is 3.141592344990079. Let's ask google:\n\npi-3.141592344990079<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"849\" height=\"298\" src=\"http:\/\/retrofocus.com\/wp-content\/uploads\/2020\/06\/Screenshot-from-2020-06-11-23-32-51_2.png\" alt=\"\" class=\"wp-image-12\" srcset=\"http:\/\/retrofocus.com\/wp-content\/uploads\/2020\/06\/Screenshot-from-2020-06-11-23-32-51_2.png 849w, http:\/\/retrofocus.com\/wp-content\/uploads\/2020\/06\/Screenshot-from-2020-06-11-23-32-51_2-300x105.png 300w, http:\/\/retrofocus.com\/wp-content\/uploads\/2020\/06\/Screenshot-from-2020-06-11-23-32-51_2-768x270.png 768w\" sizes=\"(max-width: 849px) 100vw, 849px\" \/><\/figure>\n\n\n\n<p>What next with cuda &#8211; I would like to try some financial monte carlo simulations or perhaps deoloying graph algorithms on GPU. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>What does it feel to command 1280 parallel cores? Having earlier used GPU for machine learning with Keras, I wanted to have a look at&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"http:\/\/retrofocus.com\/?p=9\">Continue reading<span class=\"screen-reader-text\">Estimating Pi using the Monte Carlo Method with Nvidia GPU<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,4],"tags":[12],"_links":{"self":[{"href":"http:\/\/retrofocus.com\/index.php?rest_route=\/wp\/v2\/posts\/9"}],"collection":[{"href":"http:\/\/retrofocus.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/retrofocus.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/retrofocus.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/retrofocus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9"}],"version-history":[{"count":8,"href":"http:\/\/retrofocus.com\/index.php?rest_route=\/wp\/v2\/posts\/9\/revisions"}],"predecessor-version":[{"id":40,"href":"http:\/\/retrofocus.com\/index.php?rest_route=\/wp\/v2\/posts\/9\/revisions\/40"}],"wp:attachment":[{"href":"http:\/\/retrofocus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/retrofocus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/retrofocus.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}