Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:33

0001 #pragma once
0002 /**
0003 SCurandState.h : More Flexible+capable replacement for SCurandState.{hh,cc}
0004 ============================================================================
0005 
0006 ::
0007 
0008     ~/o/sysrap/tests/SCurandState_test.sh
0009 
0010 
0011 Old SCurandState.{hh,cc} impl fixed num, the entire total number of photon slots. 
0012 This SCurandState.h impl moves to a chunk-centric approach in order to be: 
0013 
0014 1. extensible via addition of chunks 
0015 2. flexible by not loading all chunks with partial loading 
0016    of the last chunk if necessary to meet the runtime configured 
0017    maximum number of states.  
0018 
0019 The motivation is to allow the maxphoton to be dynamically decided 
0020 depending on VRAM by controlling the number of chunks in the available 
0021 sequence to be loaded+uploaded at runtime. 
0022  
0023 Actually there is no need for concatenation CPU side (only GPU side). 
0024 Can load and upload chunk-by-chunk to complete the contiguous 
0025 states GPU side. 
0026 
0027 
0028 Related
0029 --------
0030 
0031 qudarap/QCurandStateMonolithic.{hh,cc,cu}
0032    alloc+create+download+save
0033 
0034 sysrap/SLaunchSequence.h
0035    configure launches
0036 
0037 qudarap/QRng.{hh.cc}
0038 
0039 **/
0040 
0041 
0042 #include "SCurandChunk.h"
0043 #include "SCurandSpec.h"
0044 #include "SYSRAP_API_EXPORT.hh"
0045 #include "SCU_.h"
0046 
0047 struct SYSRAP_API SCurandState   
0048 {
0049     typedef unsigned long long ULL ;
0050     static constexpr const ULL M = 1000000ull ; 
0051 
0052     const char* dir ; 
0053     std::vector<ULL> spec = {} ; 
0054     std::vector<SCurandChunk> chunk = {} ; 
0055     scurandref<curandStateXORWOW> all = {} ; 
0056 
0057     const char* getDir() const ; 
0058 
0059     SCurandState(const char* dir=nullptr); 
0060 
0061     void init(); 
0062     void initFromSpec(); 
0063     void initFromDir();
0064     void addChunk(ULL num);
0065  
0066     ULL num_total() const ;
0067     std::string desc() const ; 
0068  
0069     bool is_complete() const ; 
0070 
0071     template<typename T>
0072     T* loadAndUpload( unsigned rngmax ) ; 
0073 
0074 }; 
0075 
0076 
0077 inline const char* SCurandState::getDir() const 
0078 {
0079     return SCurandChunk::Dir(dir); 
0080 }
0081 
0082 inline SCurandState::SCurandState(const char* _dir)
0083     :
0084     dir( _dir ? strdup(_dir) : nullptr )
0085 {
0086     init(); 
0087 }
0088 
0089 
0090 /**
0091 SCurandState::init
0092 ---------------------
0093 
0094 1. parse SPEC populating spec vector with slots per chunk values
0095 2. parse directory populating chunk vector based on file names
0096 
0097 Whether chunk files exist or not already 
0098 the outcome of this instanciation remains the same, 
0099 namely a chunk vector that follows the spec vector. 
0100 
0101 **/
0102 
0103 inline void SCurandState::init()
0104 {
0105     all = {} ; 
0106     all.chunk_idx = 0 ; 
0107     all.chunk_offset = 0 ; 
0108     all.num = 0 ;
0109     all.states = nullptr ; 
0110 
0111     const char* SEED_OFFSET = ssys::getenvvar("SCurandState__init_SEED_OFFSET"); 
0112     const char* CHUNKSIZES  = ssys::getenvvar("SCurandState__init_CHUNKSIZES"); 
0113 
0114     SCurandSpec::ParseSeedOffset( all.seed, all.offset, SEED_OFFSET ); 
0115     SCurandSpec::ParseChunkSizes( spec, CHUNKSIZES  ); 
0116 
0117     SCurandChunk::ParseDir(chunk, dir);  
0118 
0119     int num_spec = spec.size() ; 
0120     int num_chunk = chunk.size() ; 
0121 
0122     if(num_chunk == 0)   // no chunk files found
0123     {
0124         initFromSpec(); 
0125     }
0126     else if( num_chunk > 0 )
0127     {
0128         initFromDir(); 
0129     }
0130 
0131     all.num = num_total(); 
0132 
0133     assert( spec.size() == chunk.size() );
0134 }
0135 
0136 
0137 
0138 
0139 inline void SCurandState::initFromSpec()
0140 {
0141     long num_spec = spec.size() ; 
0142     for(long i=0 ; i < num_spec ; i++) 
0143     {
0144        ULL num = spec[i]; 
0145        addChunk(num);
0146     }
0147 }
0148 
0149 
0150 /**
0151 SCurandState::initFromDir
0152 ----------------------------
0153 
0154 Expecting to find the first chunks to be consistent
0155 with the spec. When SPEC is extended appropriately 
0156 then chunks will be missing from the end, and will be 
0157 added from the spec. 
0158 
0159 Changing the size SPEC is any way that does not just extend
0160 the chunks would cause this to assert. 
0161 
0162 HMM: in principal could arrange to replace missing chunks
0163 but that luxury feature has not been implemented. 
0164 
0165 
0166 * iteration over spec vector to support spec extension
0167 
0168 * note that when the directory has all the chunk files
0169   already this does nothing other than checking that 
0170   the chunks follow the spec, the work of forming 
0171   the chunks being done in the init
0172 
0173 **/
0174 
0175 inline void SCurandState::initFromDir()
0176 {
0177     ULL num_spec = spec.size() ; 
0178 
0179     for(ULL i=0 ; i < num_spec ; i++)
0180     {
0181         scurandref<curandStateXORWOW>* r = SCurandChunk::Find(chunk, i );
0182         ULL num_cumulative = SCurandChunk::NumTotal_InRange(chunk, 0, i ); 
0183         bool already_have_chunk = r != nullptr ; 
0184 
0185         if(already_have_chunk)   
0186         {
0187             bool r_chunk_follows_spec = 
0188                       r->chunk_idx == i && 
0189                       r->chunk_offset == num_cumulative && 
0190                       r->num == spec[i] &&
0191                       r->seed == all.seed &&
0192                       r->offset == all.offset 
0193                       ;  
0194 
0195             assert(r_chunk_follows_spec); 
0196             if(!r_chunk_follows_spec) std::cerr
0197                 << "SCurandState::initFromDir"
0198                 << " r_chunk_follows_spec " << ( r_chunk_follows_spec ? "YES" : "NO " ) << "\n"
0199                 << " r.chunk_idx " << r->chunk_idx << " i " << i << "\n" 
0200                 << " r.chunk_offset " << r->chunk_offset << " num_cumulative " << num_cumulative << "\n" 
0201                 << " r.num " << r->num << " spec[i] " << spec[i] << "\n"
0202                 << " r.seed " << r->seed << " all.seed " << all.seed << "\n"
0203                 << " r.offset " << r->offset << " all.offset " << all.offset << "\n"
0204                 ; 
0205 
0206             if(!r_chunk_follows_spec) return ;  
0207         } 
0208         else
0209         {
0210             addChunk(spec[i]); 
0211         }
0212     }
0213 }
0214 
0215 
0216 
0217 
0218 inline void SCurandState::addChunk(ULL num)
0219 {
0220     int num_chunk = chunk.size(); 
0221     ULL num_cumulative = SCurandChunk::NumTotal_InRange(chunk, 0, num_chunk ); 
0222 
0223     SCurandChunk c = {} ; 
0224 
0225     c.ref.chunk_idx = num_chunk ; 
0226     c.ref.chunk_offset = num_cumulative ; 
0227     c.ref.num = num ; 
0228     c.ref.seed = all.seed ; 
0229     c.ref.offset = all.offset ; 
0230     c.ref.states = nullptr ; 
0231 
0232     chunk.push_back(c); 
0233 }
0234 
0235 inline unsigned long long SCurandState::num_total() const
0236 {
0237     return SCurandChunk::NumTotal_SpecCheck(chunk, spec); 
0238 }
0239 
0240 
0241 inline std::string SCurandState::desc() const 
0242 {
0243     const char* _dir = getDir();
0244 
0245     int num_spec = spec.size(); 
0246     int num_chunk = chunk.size(); 
0247     int num_valid = SCurandChunk::CountValid(chunk, dir );  
0248     bool complete = is_complete(); 
0249 
0250     std::stringstream ss; 
0251     ss 
0252        << "[SCurandState::desc\n" 
0253        << " dir " << ( dir ? dir : "-" ) << "\n" 
0254        << " getDir " << ( _dir ? _dir : "-" ) << "\n" 
0255        << " num_spec " << num_spec  << "\n" 
0256        << " num_chunk " << num_chunk << "\n" 
0257        << " num_valid " << num_valid << "\n" 
0258        << " is_complete " << ( complete ? "YES" : "NO " ) << "\n" 
0259        << " num_total " << SCurandChunk::FormatNum(num_total()) << "\n"
0260        << SCurandSpec::Desc(spec)  
0261        << "\n"  
0262        << SCurandChunk::Desc(chunk, dir) 
0263        << "]SCurandState::desc\n" 
0264        ; 
0265     std::string str = ss.str(); 
0266     return str ;   
0267 } 
0268 
0269 inline bool SCurandState::is_complete() const
0270 {
0271     int num_spec = spec.size(); 
0272     int num_valid = SCurandChunk::CountValid(chunk, dir );  
0273     return num_spec == num_valid ; 
0274 } 
0275 
0276 
0277 /**
0278 SCurandState::loadAndUpload
0279 ----------------------------
0280 
0281 This is intended to replace QRng::LoadAndUpload 
0282 
0283 **/
0284 
0285 
0286 template<typename T>
0287 inline T* SCurandState::loadAndUpload( unsigned rngmax )
0288 {
0289     T* d0 = SCU_::device_alloc<T>( rngmax, "SCurandState::loadAndUpload" );
0290     T* d = d0 ; 
0291 
0292     ULL available_chunk = chunk.size(); 
0293     ULL count = 0 ; 
0294 
0295     std::cout
0296         << " rngmax " << rngmax
0297         << " rngmax/M " << rngmax/M
0298         << " available_chunk " << available_chunk 
0299         << " all.num/M " << all.num/M 
0300         << " rngmax/M " << rngmax/M
0301         << " d0 " << d0  
0302         << "\n"
0303         ;
0304 
0305 
0306     sdigest dig ; 
0307 
0308     for(ULL i=0 ; i < available_chunk ; i++)
0309     {   
0310         ULL remaining = rngmax - count ;   
0311 
0312         const SCurandChunk& ck = chunk[i]; 
0313  
0314         bool partial_read = remaining < ck.ref.num ;   
0315 
0316         ULL num = partial_read ? remaining : ck.ref.num ;
0317 
0318         std::cout 
0319             << " i " << std::setw(3) << i 
0320             << " ck.ref.num/M " << std::setw(4) << ck.ref.num/M
0321             << " count/M " << std::setw(4) << count/M
0322             << " remaining/M " << std::setw(4) << remaining/M
0323             << " partial_read " << ( partial_read ? "YES" : "NO " )
0324             << " num/M " << std::setw(4) << num/M
0325             << " d " << d 
0326             << "\n"
0327             ;
0328 
0329         scurandref<curandStateXORWOW> cr = ck.load(num, dir, &dig ) ; 
0330   
0331         assert( cr.states != nullptr); 
0332 
0333         bool num_match = cr.num == num ; 
0334 
0335         if(!num_match) std::cerr
0336             << "SCurandState::loadAndUpload"
0337             << " num_match " << ( num_match ? "YES" : "NO " )
0338             << " cr.num/M " << cr.num/M
0339             << " num/M " << num/M
0340             << "\n"
0341             ;
0342 
0343         assert(num_match); 
0344 
0345         SCU_::copy_host_to_device<T>( d , cr.states , num );  
0346 
0347         free(cr.states); 
0348 
0349         d += num ;   
0350         count += num ;   
0351 
0352         if(count > rngmax) assert(0); 
0353         if(count == rngmax) break ;
0354     }   
0355 
0356     bool complete = count == rngmax ;
0357     assert( complete );
0358     std::string digest = dig.finalize();
0359 
0360     std::cout
0361         << "SCurandState::loadAndUpload"
0362         << " complete " << ( complete ? "YES" : "NO ")
0363         << " rngmax/M " << rngmax/M
0364         << " rngmax " << rngmax
0365         << " digest " << digest << "(cf md5sum of cat-ed chunk(s))" 
0366         << "\n"
0367         ;
0368 
0369     return complete ? d0 : nullptr ;
0370 }
0371 
0372 
0373