benchmark.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //RandomX performance test for x86
  2. //https://github.com/tevador/RandomX
  3. //License: GPL v3
  4. #include <cstdint>
  5. #include <random>
  6. #include <iostream>
  7. #include <chrono>
  8. #include <sstream>
  9. #include <cmath>
  10. #include <cstring>
  11. #if defined(_WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
  12. #define WINDOWS
  13. #include <io.h>
  14. #include <fcntl.h>
  15. #endif
  16. #if defined(__GNUC__) && defined(__x86_64__)
  17. #include <x86intrin.h>
  18. typedef unsigned __int128 uint128_t;
  19. typedef __int128 int128_t;
  20. static inline uint64_t umulhi64(uint64_t a, uint64_t b) {
  21. return ((uint128_t)a * b) >> 64;
  22. }
  23. static inline uint64_t imulhi64(int64_t a, int64_t b) {
  24. return ((int128_t)a * b) >> 64;
  25. }
  26. #define ror64 __rorq
  27. #define rol64 __rolq
  28. #define forceinline inline
  29. #elif defined(_MSC_VER) && defined(_M_X64)
  30. #include <intrin.h>
  31. #include <stdlib.h>
  32. #define umulhi64 __umulh
  33. static inline uint64_t imulhi64(int64_t a, int64_t b) {
  34. int64_t hi;
  35. _mul128(a, b, &hi);
  36. return hi;
  37. }
  38. #define ror64 _rotr64
  39. #define rol64 _rotl64
  40. #define forceinline __forceinline
  41. #else
  42. #error "Unsupported platform"
  43. #endif
  44. typedef union {
  45. double f64;
  46. int64_t i64;
  47. uint64_t u64;
  48. int32_t i32;
  49. uint32_t u32;
  50. } convertible_t;
  51. forceinline void NOOP(convertible_t& a, convertible_t& b, convertible_t& c) {
  52. c.u64 = a.u64;
  53. }
  54. forceinline void FNOOP(convertible_t& a, convertible_t& b, convertible_t& c) {
  55. c.f64 = (double)a.i64;
  56. }
  57. forceinline void ADD_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  58. c.u64 = a.u64 + b.u64;
  59. }
  60. forceinline void ADD_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  61. c.u64 = a.u32 + b.u32;
  62. }
  63. forceinline void SUB_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  64. c.u64 = a.u64 - b.u64;
  65. }
  66. forceinline void SUB_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  67. c.u64 = a.u32 - b.u32;
  68. }
  69. forceinline void MUL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  70. c.u64 = a.u64 * b.u64;
  71. }
  72. forceinline void MULH_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  73. c.u64 = umulhi64(a.u64, b.u64);
  74. }
  75. forceinline void MUL_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  76. c.u64 = (uint64_t)a.u32 * b.u32;
  77. }
  78. forceinline void IMUL_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  79. c.i64 = (int64_t)a.i32 * b.i32;
  80. }
  81. forceinline void IMULH_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  82. c.i64 = imulhi64(a.i64, b.i64);
  83. }
  84. forceinline void DIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  85. c.u64 = a.u64 / (b.u32 != 0 ? b.u32 : 1U);
  86. }
  87. forceinline void IDIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  88. c.i64 = a.i64 / (b.i32 != 0 ? b.i32 : 1);
  89. }
  90. forceinline void AND_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  91. c.u64 = a.u64 & b.u64;
  92. }
  93. forceinline void AND_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  94. c.u64 = a.u32 & b.u32;
  95. }
  96. forceinline void OR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  97. c.u64 = a.u64 | b.u64;
  98. }
  99. forceinline void OR_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  100. c.u64 = a.u32 | b.u32;
  101. }
  102. forceinline void XOR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  103. c.u64 = a.u64 ^ b.u64;
  104. }
  105. forceinline void XOR_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  106. c.u64 = a.u32 ^ b.u32;
  107. }
  108. forceinline void SHL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  109. c.u64 = a.u64 << (b.u64 & 63);
  110. }
  111. forceinline void SHR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  112. c.u64 = a.u64 >> (b.u64 & 63);
  113. }
  114. forceinline void SAR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  115. c.i64 = a.i64 >> (b.u64 & 63);
  116. }
  117. forceinline void ROL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  118. c.u64 = rol64(a.u64, (b.u64 & 63));
  119. }
  120. forceinline void ROR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  121. c.u64 = ror64(a.u64, (b.u64 & 63));
  122. }
  123. forceinline void FADD(convertible_t& a, convertible_t& b, convertible_t& c) {
  124. c.f64 = (double)a.i64 + (double)b.i64;
  125. }
  126. forceinline void FSUB(convertible_t& a, convertible_t& b, convertible_t& c) {
  127. c.f64 = (double)a.i64 - (double)b.i64;
  128. }
  129. forceinline void FMUL(convertible_t& a, convertible_t& b, convertible_t& c) {
  130. c.f64 = (double)a.i64 * (double)b.i64;
  131. }
  132. forceinline void FDIV(convertible_t& a, convertible_t& b, convertible_t& c) {
  133. c.f64 = (double)a.i64 / (double)b.i64;
  134. }
  135. forceinline void FSQRT(convertible_t& a, convertible_t& b, convertible_t& c) {
  136. double d = fabs((double)a.i64);
  137. c.f64 = _mm_cvtsd_f64(_mm_sqrt_sd(_mm_setzero_pd(), _mm_load_pd(&d)));
  138. }
  139. static uint32_t mxcsr;
  140. forceinline void FROUND(convertible_t& a, convertible_t& b, convertible_t& c) {
  141. c.f64 = (double)a.i64;
  142. _mm_setcsr(mxcsr | ((uint32_t)(a.u64 << 13) & _MM_ROUND_MASK));
  143. }
  144. inline void init_FPU() {
  145. mxcsr = (_mm_getcsr() | _MM_FLUSH_ZERO_ON) & ~_MM_ROUND_MASK;
  146. _mm_setcsr(mxcsr);
  147. }
  148. template<typename T>
  149. bool tryParse(char* buffer, T& out) {
  150. std::istringstream ss(buffer);
  151. if (!(ss >> out)) {
  152. std::cout << "Invalid value '" << buffer << "'" << std::endl;
  153. return false;
  154. }
  155. return true;
  156. }
  157. //#define ITERATIONS 10000000
  158. #define SCRATCHPAD_SIZE (16 * 1024)
  159. #define SCRATCHPAD_LENGTH (SCRATCHPAD_SIZE / sizeof(convertible_t))
  160. #define SCRATCHPAD_MASK (SCRATCHPAD_SIZE / sizeof(convertible_t) - 1)
  161. #define SCRATCHPAD_16K(x) scratchpad[(x) & SCRATCHPAD_MASK]
  162. #define BENCHMARK(FUNC,TYPE) do { \
  163. memcpy((void*)scratchpad, input, SCRATCHPAD_SIZE); \
  164. tstart = std::chrono::high_resolution_clock::now(); \
  165. for (uint64_t i = 0; i < iterations; ++i) { \
  166. FUNC(SCRATCHPAD_16K(i + 8 + 0), r0, SCRATCHPAD_16K(i + 0)); \
  167. SCRATCHPAD_16K(i + 0).u64 ^= r7.u64;\
  168. FUNC(SCRATCHPAD_16K(i + 8 + 1), r1, SCRATCHPAD_16K(i + 1)); \
  169. SCRATCHPAD_16K(i + 1).u64 ^= r6.u64;\
  170. FUNC(SCRATCHPAD_16K(i + 8 + 2), r2, SCRATCHPAD_16K(i + 2)); \
  171. SCRATCHPAD_16K(i + 2).u64 ^= r5.u64;\
  172. FUNC(SCRATCHPAD_16K(i + 8 + 3), r3, SCRATCHPAD_16K(i + 3)); \
  173. SCRATCHPAD_16K(i + 3).u64 ^= r4.u64;\
  174. FUNC(SCRATCHPAD_16K(i + 8 + 4), r4, SCRATCHPAD_16K(i + 4)); \
  175. SCRATCHPAD_16K(i + 4).u64 ^= r3.u64;\
  176. FUNC(SCRATCHPAD_16K(i + 8 + 5), r5, SCRATCHPAD_16K(i + 5)); \
  177. SCRATCHPAD_16K(i + 5).u64 ^= r2.u64;\
  178. FUNC(SCRATCHPAD_16K(i + 8 + 6), r6, SCRATCHPAD_16K(i + 6)); \
  179. SCRATCHPAD_16K(i + 6).u64 ^= r1.u64;\
  180. FUNC(SCRATCHPAD_16K(i + 8 + 7), r7, SCRATCHPAD_16K(i + 7)); \
  181. SCRATCHPAD_16K(i + 7).u64 ^= r0.u64;\
  182. } \
  183. tend = std::chrono::high_resolution_clock::now(); \
  184. uint64_t acum = 0; \
  185. for (int i = 0; i < SCRATCHPAD_LENGTH; ++i) \
  186. acum += scratchpad[i].u64; \
  187. std::cout << "| " << #FUNC << " | " << std::chrono::duration<double>(tend - tstart).count() << " | " << acum << " |" << std::endl; \
  188. } while(false)
  189. int main(int argc, char** argv) {
  190. uint64_t iterations;
  191. if (argc > 1) {
  192. if (!tryParse(argv[1], iterations))
  193. return 1;
  194. }
  195. else {
  196. iterations = 100000000;
  197. }
  198. #ifdef WINDOWS
  199. _setmode(_fileno(stdin), O_BINARY);
  200. #endif
  201. convertible_t input[SCRATCHPAD_LENGTH];
  202. std::cout << "Reading " << sizeof(input) << " bytes from STDIN..." << std::endl;
  203. std::cin.read((char*)input, sizeof(input));
  204. if (!std::cin) {
  205. std::cerr << "Insufficient input" << std::endl;
  206. return 1;
  207. }
  208. convertible_t scratchpad[SCRATCHPAD_LENGTH];
  209. convertible_t r0, r1, r2, r3, r4, r5, r6, r7;
  210. r0.u64 = input[0].u64;
  211. r1.u64 = input[1].u64;
  212. r2.u64 = input[2].u64;
  213. r3.u64 = input[3].u64;
  214. r4.u64 = input[4].u64;
  215. r5.u64 = input[5].u64;
  216. r6.u64 = input[6].u64;
  217. r7.u64 = input[7].u64;
  218. std::chrono::high_resolution_clock::time_point tstart, tend;
  219. std::cout << iterations << " iterations:" << std::endl << std::endl;
  220. std::cout << "| operation | time [s] | (result) |" << std::endl;
  221. std::cout << "|-----------|----------|----------|" << std::endl;
  222. BENCHMARK(NOOP, u64);
  223. BENCHMARK(ADD_64, u64);
  224. BENCHMARK(ADD_32, u64);
  225. BENCHMARK(SUB_64, u64);
  226. BENCHMARK(SUB_32, u64);
  227. BENCHMARK(MUL_64, u64);
  228. BENCHMARK(MULH_64, u64);
  229. BENCHMARK(MUL_32, u64);
  230. BENCHMARK(IMUL_32, u64);
  231. BENCHMARK(IMULH_64, u64);
  232. BENCHMARK(DIV_64, u64);
  233. BENCHMARK(IDIV_64, u64);
  234. BENCHMARK(AND_64, u64);
  235. BENCHMARK(AND_32, u64);
  236. BENCHMARK(OR_64, u64);
  237. BENCHMARK(OR_32, u64);
  238. BENCHMARK(XOR_64, u64);
  239. BENCHMARK(XOR_32, u64);
  240. BENCHMARK(SHL_64, u64);
  241. BENCHMARK(SHR_64, u64);
  242. BENCHMARK(SAR_64, u64);
  243. BENCHMARK(ROR_64, u64);
  244. BENCHMARK(ROL_64, u64);
  245. init_FPU();
  246. BENCHMARK(FNOOP, f64);
  247. BENCHMARK(FADD, f64);
  248. BENCHMARK(FSUB, f64);
  249. BENCHMARK(FMUL, f64);
  250. BENCHMARK(FDIV, f64);
  251. BENCHMARK(FSQRT, f64);
  252. BENCHMARK(FROUND, f64);
  253. return 0;
  254. }