random123

Counter-based Random Number Generators
git clone git://git.meso-star.com/random123.git
Log | Files | Refs | README | LICENSE

timers.cpp (7425B)


      1 /*
      2 Copyright 2010-2011, D. E. Shaw Research.
      3 All rights reserved.
      4 
      5 Redistribution and use in source and binary forms, with or without
      6 modification, are permitted provided that the following conditions are
      7 met:
      8 
      9 * Redistributions of source code must retain the above copyright
     10   notice, this list of conditions, and the following disclaimer.
     11 
     12 * Redistributions in binary form must reproduce the above copyright
     13   notice, this list of conditions, and the following disclaimer in the
     14   documentation and/or other materials provided with the distribution.
     15 
     16 * Neither the name of D. E. Shaw Research nor the names of its
     17   contributors may be used to endorse or promote products derived from
     18   this software without specific prior written permission.
     19 
     20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 */
     32 
     33 #include "util.h"
     34 
     35 #include <Random123/philox.h>
     36 #include <Random123/threefry.h>
     37 #include <Random123/aes.h>
     38 #include <Random123/ars.h>
     39 #include <cstdio>
     40 #include <cmath>
     41 #include <string>
     42 #include <iostream>
     43 #include <sstream>
     44 #include <cstring>
     45 #if R123_USE_X86INTRIN_H
     46 #include <x86intrin.h>
     47 #endif
     48 #include "util_demangle.hpp"
     49 #include "util.h"
     50 
     51 using namespace r123;
     52 
     53 const char *progname;
     54 int debug = 0;
     55 double cpu_hz = -1.0;
     56 
     57 using namespace std;
     58 
     59 namespace{
     60     template <typename B> void timer();
     61 } // namespace <anon>
     62 
     63 int main(int argc, char **argv){
     64     progname = argv[0];
     65 
     66     const char *envp;
     67     if( (envp = getenv("TIMERS_CPU_GHZ"))){
     68         cpu_hz = 1.e9 * atof(envp);
     69     }
     70 #if R123_USE_AES_NI
     71     if( argc == 1 || strcmp(argv[1], "ARS")==0 ){
     72     if(haveAESNI()){
     73         timer<ARS1xm128i_R<5> >();
     74         timer<ARS4x32_R<5> >();
     75         timer<ARS1xm128i_R<7> >();
     76         timer<ARS4x32_R<7> >();
     77         timer<ARS1xm128i_R<10> >();
     78         timer<AESNI1xm128i >();
     79     }else{
     80         cout << "Skipping Bijections that use AES-NI instructions that are not available on this platform\n";
     81     } 
     82     }
     83 #else
     84     cout << "This binary is not compiled with AES-NI support.  Skipping the ARS bijections\n";
     85 #endif // R123_USE_AES_NI
     86 
     87     if( argc == 1 || strcmp(argv[1], "AES")==0 ){
     88 #if R123_USE_AES_OPENSSL
     89     cout << "\n";
     90     timer<AESOpenSSL16x8>();
     91 #endif
     92     }
     93 
     94     if( argc == 1 || strcmp(argv[1], "Threefry4x32")==0 ) {
     95     cout << "\n";
     96     timer<Threefry4x32_R<12> >();
     97     timer<Threefry4x32_R<20> >();
     98     timer<Threefry4x32 >();
     99     }
    100 
    101 #if R123_USE_64BIT
    102     if( argc == 1 || strcmp(argv[1], "Threefry2x64")==0 ){
    103     cout << "\n";
    104     timer<Threefry2x64_R<13> >();
    105     timer<Threefry2x64_R<20> >();
    106     timer<Threefry2x64 >();
    107     }
    108 
    109     if( argc == 1 || strcmp(argv[1], "Threefry4x64")==0 ){
    110     cout << "\n";
    111     timer<Threefry4x64_R<12> >();
    112     timer<Threefry4x64_R<20> >();
    113     timer<Threefry4x64 >();
    114     timer<Threefry4x64_R<72> >();
    115     }
    116 #else
    117     cout << "\n64bit types not implemented.  Skipping Threefry-Nx64 bijections\n";
    118 #endif
    119 
    120 #if R123_USE_PHILOX_64BIT
    121     if( argc == 1 || strcmp(argv[1], "Philox2x64") == 0 ){
    122     cout << "\n";
    123     timer<Philox2x64_R<6> >();
    124     timer<Philox2x64_R<10> >();
    125     }
    126 
    127     if( argc == 1 || strcmp(argv[1], "Philox4x64") == 0 ){
    128     cout << "\n";
    129     timer<Philox4x64_R<7> >();
    130     timer<Philox4x64_R<10> >();
    131     }
    132 #else
    133     cout << "\n64x64->128bit multiplication is not implmented.  Skipping Philox-Nx64 bijections\n";
    134 #endif
    135 
    136     if( argc == 1 || strcmp(argv[1], "Philox4x32") == 0 ){
    137     cout << "\n";
    138     timer<Philox4x32_R<7> >();
    139     timer<Philox4x32_R<10> >();
    140     }
    141 
    142     return 0;
    143 }
    144 
    145     
    146 namespace{
    147 
    148 // To prevent the compiler from noticing that the result of the
    149 // bijection is never used and eliding the entire calculation, we
    150 // accumulate the output of millions of calls to the bijection.  All
    151 // the ctr_types are sufficiently container-like that we can just
    152 // loop over the contents, doing += on each value_type
    153 template <typename CtrType>
    154 CtrType& operator+=(CtrType& lhs, CtrType rhs){ 
    155     typename CtrType::const_iterator rp = rhs.cbegin();
    156     for(typename CtrType::iterator lp=lhs.begin(); lp!=lhs.end(); ++lp)
    157             *lp ^= *rp++;
    158         return lhs; 
    159 }
    160 
    161 // We've accumulated it, but we still have to use it.  A non-zero
    162 // test serves the purpose:
    163 template <typename CtrType>
    164 bool nz(const CtrType v){
    165     for(typename CtrType::const_iterator vp=v.cbegin(); vp!=v.cend(); ++vp)
    166         if( *vp ) return true;
    167     return false;
    168 }
    169 
    170 #if R123_USE_AES_NI
    171 // The "obvious" solution for ctr_types whose value_type doesn't
    172 // have += defined (e.g., m128i) would be to overload += on the
    173 // value_type.  But we can't do that in gcc because m128i is typedefed
    174 // to a fancy compiler-specific builtin type, and you can only overload
    175 // += on classes and enums.  So instead we specialize += on the
    176 // array instead of on the value_type:
    177 template<> 
    178 r123array1xm128i& operator+=(r123array1xm128i& lhs, r123array1xm128i rhs){
    179     typedef r123array1xm128i CtrType;
    180     CtrType::const_iterator rp = rhs.cbegin();
    181     for(CtrType::iterator lp=lhs.begin(); lp!=lhs.end(); ++lp)
    182         lp->m = _mm_xor_si128(*lp, *rp++);
    183     return lhs;
    184 }
    185 #endif
    186 
    187 template <typename B>
    188 void timer(){
    189     typedef typename B::ctr_type ctr_type;
    190     ctr_type sum = {{}};
    191     uint_fast64_t N = 1000000;    // First try only a few thousand...
    192     B b;
    193     int bytes_per_call = sizeof(ctr_type);
    194     cout << demangle(b) << ": gran: " << bytes_per_call;
    195 
    196     ctr_type c0 = {{}};
    197     const char *envp;
    198     if((envp = getenv("TIMERS_COUNTER"))){
    199         std::istringstream iss((std::string(envp)));
    200         iss >> c0;
    201     }
    202      
    203     typename B::ukey_type uk = {{}};
    204     if( (envp = getenv("TIMERS_KEY"))){
    205         std::istringstream iss((std::string(envp)));
    206         iss >> uk;
    207     }
    208     typename B::key_type k(uk);
    209    
    210     ctr_type c = c0;
    211     double clk;
    212     ::timer(&clk);
    213     for(uint_fast64_t i=0; i<N; ++i){
    214         c.incr();
    215         sum += b(c, k);
    216     }
    217     double dur = ::timer(&clk);
    218 
    219     double bestrate = 0.;
    220     double bestdur = 0.;
    221     uint_fast64_t bestN = 0;
    222     for(size_t t=0; t<5; ++t){
    223         ctr_type c = c0;
    224         N = (uint_fast64_t)(N*(0.1/dur));
    225         ::timer(&clk);
    226         for(uint_fast64_t i=0; i<N; ++i){
    227             c.incr();
    228             sum += b(c, k);
    229         }
    230         dur = ::timer(&clk);
    231         double rate = N*bytes_per_call/dur;
    232         if( rate > bestrate ){
    233             bestrate = rate;
    234             bestN = N;
    235             bestdur = dur;
    236         }
    237     }
    238     cout << " (best of 5) " << bestN << " bijections in " << bestdur << " sec. rate: " << bestrate*1.e-9 << "GB/s";
    239     if( cpu_hz > 0. )
    240         cout << " cpB: " << cpu_hz/bestrate;
    241     cout << endl;
    242         
    243     if(!nz(sum))
    244         cout << "Don't let the compiler optimize it all away... sum==0.  That's a surprise!\n";
    245 }
    246 
    247 } // namespace <anonymous>
    248 
    249