CPP   126

rng.cpp

Guest on 28th June 2022 10:13:30 AM

  1. /*
  2.  * Copyright  NVIDIA Corporation.  All rights reserved.
  3.  *
  4.  * Please refer to the NVIDIA end user license agreement (EULA) associated
  5.  * with this source code for terms and conditions that govern your use of
  6.  * this software. Any use, reproduction, disclosure, or distribution of
  7.  * this software and related documentation outside the terms of the EULA
  8.  * is strictly prohibited.
  9.  *
  10.  */
  11.  
  12. /*
  13. * Copyright  NVIDIA Corporation.  All rights reserved.
  14. *
  15. * NVIDIA Corporation and its licensors retain all intellectual property and
  16. * proprietary rights in and to this software and related documentation.
  17. * Any use, reproduction, disclosure, or distribution of this software
  18. * and related documentation without an express license agreement from
  19. * NVIDIA Corporation is strictly prohibited.
  20. *
  21. * Please refer to the applicable NVIDIA end user license agreement (EULA)
  22. * associated with this source code for terms and conditions that govern
  23. * your use of this NVIDIA software.
  24. *
  25. */
  26.  
  27. // Utilities and System includes
  28. #include <helper_cuda.h>
  29.  
  30. // Includes
  31. #include <curand.h>
  32. #include <stdexcept>
  33. #include <sstream>
  34. #include "rng.h"
  35.  
  36. // Shared Library Test Functions
  37. #include <helper_timer.h>
  38.  
  39. const unsigned int RNG::s_maxQrngDimensions = 20000;
  40.  
  41. RNG::RNG(unsigned long prngSeed, unsigned int qrngDimensions, unsigned int nSamples)
  42.     : m_prngSeed(prngSeed),
  43.       m_qrngDimensions(qrngDimensions),
  44.       m_nSamplesBatchTarget(nSamples),
  45.       m_nSamplesRemaining(0)
  46. {
  47.     using std::string;
  48.     using std::runtime_error;
  49.     using std::invalid_argument;
  50.  
  51.     if (m_prngSeed == 0)
  52.     {
  53.         throw invalid_argument("PRNG seed must be non-zero");
  54.     }
  55.  
  56.     if (m_qrngDimensions == 0)
  57.     {
  58.         throw invalid_argument("QRNG dimensions must be non-zero");
  59.     }
  60.  
  61.     if (m_nSamplesBatchTarget == 0)
  62.     {
  63.         throw invalid_argument("RNG batch size must be non-zero");
  64.     }
  65.  
  66.     if (m_nSamplesBatchTarget < s_maxQrngDimensions)
  67.     {
  68.         throw invalid_argument("RNG batch size must be greater than RNG::s_maxQrngDimensions");
  69.     }
  70.  
  71.     curandStatus_t curandResult;
  72.     cudaError_t    cudaResult;
  73.  
  74.     // Allocate sample array in host mem
  75.     m_h_samples = (float *)malloc(m_nSamplesBatchTarget * sizeof(float));
  76.  
  77.     if (m_h_samples == NULL)
  78.     {
  79.         throw runtime_error("Could not allocate host memory for RNG::m_h_samples");
  80.     }
  81.  
  82.     // Allocate sample array in device mem
  83.     cudaResult = cudaMalloc((void **)&m_d_samples, m_nSamplesBatchTarget * sizeof(float));
  84.  
  85.     if (cudaResult != cudaSuccess)
  86.     {
  87.         string msg("Could not allocate device memory for RNG::m_d_samples: ");
  88.         msg += cudaGetErrorString(cudaResult);
  89.         throw runtime_error(msg);
  90.     }
  91.  
  92.     // Create the Random Number Generators
  93.     curandResult = curandCreateGenerator(&m_prng, CURAND_RNG_PSEUDO_XORWOW);
  94.  
  95.     if (curandResult != CURAND_STATUS_SUCCESS)
  96.     {
  97.         string msg("Could not create pseudo-random number generator: ");
  98.         msg += curandResult;
  99.         throw runtime_error(msg);
  100.     }
  101.  
  102.     curandResult = curandCreateGenerator(&m_qrng, CURAND_RNG_QUASI_SOBOL32);
  103.  
  104.     if (curandResult != CURAND_STATUS_SUCCESS)
  105.     {
  106.         string msg("Could not create quasi-random number generator: ");
  107.         msg += curandResult;
  108.         throw runtime_error(msg);
  109.     }
  110.  
  111.     curandResult = curandCreateGenerator(&m_sqrng, CURAND_RNG_QUASI_SCRAMBLED_SOBOL32);
  112.  
  113.     if (curandResult != CURAND_STATUS_SUCCESS)
  114.     {
  115.         string msg("Could not create scrambled quasi-random number generator: ");
  116.         msg += curandResult;
  117.         throw runtime_error(msg);
  118.     }
  119.  
  120.  
  121.     // Setup initial parameters
  122.     resetSeed();
  123.     updateDimensions();
  124.     setBatchSize();
  125.  
  126.     // Set default RNG to be pseudo-random (XORWOW)
  127.     m_pCurrent = &m_prng;
  128. }
  129.  
  130. RNG::~RNG()
  131. {
  132.     curandDestroyGenerator(m_prng);
  133.     curandDestroyGenerator(m_qrng);
  134.     curandDestroyGenerator(m_sqrng);
  135.  
  136.     if (m_d_samples)
  137.     {
  138.         cudaFree(m_d_samples);
  139.     }
  140.  
  141.     if (m_h_samples)
  142.     {
  143.         free(m_h_samples);
  144.     }
  145.  
  146.     // cudaDeviceReset causes the driver to clean up all state. While
  147.     // not mandatory in normal operation, it is good practice.  It is also
  148.     // needed to ensure correct operation when the application is being
  149.     // profiled. Calling cudaDeviceReset causes all profile data to be
  150.     // flushed before the application exits
  151.     cudaDeviceReset();
  152. }
  153.  
  154. void RNG::generateBatch(void)
  155. {
  156.     using std::string;
  157.     using std::runtime_error;
  158.  
  159.     cudaError_t    cudaResult;
  160.     curandStatus_t curandResult;
  161.  
  162.     // Generate random numbers
  163.     curandResult = curandGenerateUniform(*m_pCurrent, m_d_samples, m_nSamplesBatchActual);
  164.  
  165.     if (curandResult != CURAND_STATUS_SUCCESS)
  166.     {
  167.         string msg("Could not generate random numbers: ");
  168.         msg += curandResult;
  169.         throw runtime_error(msg);
  170.     }
  171.  
  172.     // Copy random numbers to host
  173.     cudaResult = cudaMemcpy(m_h_samples, m_d_samples, m_nSamplesBatchActual * sizeof(float), cudaMemcpyDeviceToHost);
  174.  
  175.     if (cudaResult != cudaSuccess)
  176.     {
  177.         string msg("Could not copy random numbers to host: ");
  178.         msg += cudaGetErrorString(cudaResult);
  179.         throw runtime_error(msg);
  180.     }
  181. }
  182.  
  183. float RNG::getNextU01(void)
  184. {
  185.     if (m_nSamplesRemaining == 0)
  186.     {
  187.         generateBatch();
  188.         m_nSamplesRemaining = m_nSamplesBatchActual;
  189.     }
  190.  
  191.     if (m_pCurrent == &m_prng)
  192.     {
  193.         return m_h_samples[m_nSamplesBatchActual - m_nSamplesRemaining--];
  194.     }
  195.     else
  196.     {
  197.         unsigned int index         = m_nSamplesBatchActual - m_nSamplesRemaining--;
  198.         unsigned int samplesPerDim = m_nSamplesBatchActual / m_qrngDimensions;
  199.         unsigned int dimOffset     = (index % m_qrngDimensions) * samplesPerDim;
  200.         unsigned int drawOffset    = index / m_qrngDimensions;
  201.         return m_h_samples[dimOffset + drawOffset];
  202.     }
  203. }
  204.  
  205. void RNG::getInfoString(std::string &msg)
  206. {
  207.     using std::stringstream;
  208.  
  209.     stringstream ss;
  210.  
  211.     if (m_pCurrent == &m_prng)
  212.     {
  213.         ss << "XORWOW (seed=" << m_prngSeed << ")";
  214.     }
  215.     else if (m_pCurrent == &m_qrng)
  216.     {
  217.         ss << "Sobol (dimensions=" << m_qrngDimensions << ")";
  218.     }
  219.     else if (m_pCurrent == &m_sqrng)
  220.     {
  221.         ss << "Scrambled Sobol (dimensions=" << m_qrngDimensions << ")";
  222.     }
  223.     else
  224.     {
  225.         ss << "Invalid RNG";
  226.     }
  227.  
  228.     msg.assign(ss.str());
  229. }
  230.  
  231. void RNG::selectRng(RNG::RngType type)
  232. {
  233.     switch (type)
  234.     {
  235.         case Quasi:
  236.             m_pCurrent = &m_qrng;
  237.             break;
  238.  
  239.         case ScrambledQuasi:
  240.             m_pCurrent = &m_sqrng;
  241.             break;
  242.  
  243.         case Pseudo:
  244.         default:
  245.             m_pCurrent = &m_prng;
  246.             break;
  247.     }
  248.  
  249.     setBatchSize();
  250. }
  251.  
  252. void RNG::resetSeed(void)
  253. {
  254.     using std::runtime_error;
  255.  
  256.     curandStatus_t curandResult;
  257.     curandResult = curandSetPseudoRandomGeneratorSeed(m_prng, m_prngSeed);
  258.  
  259.     if (curandResult != CURAND_STATUS_SUCCESS)
  260.     {
  261.         std::string msg("Could not set pseudo-random number generator seed: ");
  262.         msg += curandResult;
  263.         throw runtime_error(msg);
  264.     }
  265.  
  266.     curandResult = curandSetGeneratorOffset(m_prng, 0);
  267.  
  268.     if (curandResult != CURAND_STATUS_SUCCESS)
  269.     {
  270.         std::string msg("Could not set pseudo-random number generator offset: ");
  271.         msg += curandResult;
  272.         throw runtime_error(msg);
  273.     }
  274.  
  275.     setBatchSize();
  276. }
  277.  
  278. void RNG::resetDimensions(void)
  279. {
  280.     m_qrngDimensions = 1;
  281.     updateDimensions();
  282.     setBatchSize();
  283. }
  284.  
  285. void RNG::incrementDimensions(void)
  286. {
  287.     if (++m_qrngDimensions > s_maxQrngDimensions)
  288.     {
  289.         m_qrngDimensions = 1;
  290.     }
  291.  
  292.     updateDimensions();
  293.     setBatchSize();
  294. }
  295.  
  296. void RNG::updateDimensions(void)
  297. {
  298.     using std::runtime_error;
  299.  
  300.     curandStatus_t curandResult;
  301.     curandResult = curandSetQuasiRandomGeneratorDimensions(m_qrng, m_qrngDimensions);
  302.  
  303.     if (curandResult != CURAND_STATUS_SUCCESS)
  304.     {
  305.         std::string msg("Could not set quasi-random number generator dimensions: ");
  306.         msg += curandResult;
  307.         throw runtime_error(msg);
  308.     }
  309.  
  310.     curandResult = curandSetGeneratorOffset(m_qrng, 0);
  311.  
  312.     if (curandResult != CURAND_STATUS_SUCCESS)
  313.     {
  314.         std::string msg("Could not set quasi-random number generator offset: ");
  315.         msg += curandResult;
  316.         throw runtime_error(msg);
  317.     }
  318.  
  319.     curandResult = curandSetQuasiRandomGeneratorDimensions(m_sqrng, m_qrngDimensions);
  320.  
  321.     if (curandResult != CURAND_STATUS_SUCCESS)
  322.     {
  323.         std::string msg("Could not set scrambled quasi-random number generator dimensions: ");
  324.         msg += curandResult;
  325.         throw runtime_error(msg);
  326.     }
  327.  
  328.     curandResult = curandSetGeneratorOffset(m_sqrng, 0);
  329.  
  330.     if (curandResult != CURAND_STATUS_SUCCESS)
  331.     {
  332.         std::string msg("Could not set scrambled quasi-random number generator offset: ");
  333.         msg += curandResult;
  334.         throw runtime_error(msg);
  335.     }
  336. }
  337.  
  338. void RNG::setBatchSize(void)
  339. {
  340.     if (m_pCurrent == &m_prng)
  341.     {
  342.         m_nSamplesBatchActual = m_nSamplesBatchTarget;
  343.     }
  344.     else
  345.     {
  346.         m_nSamplesBatchActual = (m_nSamplesBatchTarget / m_qrngDimensions) * m_qrngDimensions;
  347.     }
  348.  
  349.     m_nSamplesRemaining = 0;
  350. }

Raw Paste


Login or Register to edit or fork this paste. It's free.