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