benchmark.cpp 8.6 KB

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