dataset.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. Copyright (c) 2018 tevador
  3. This file is part of RandomX.
  4. RandomX is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. RandomX is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with RandomX. If not, see<http://www.gnu.org/licenses/>.
  14. */
  15. #include <new>
  16. #include <algorithm>
  17. #include <stdexcept>
  18. #include <cstring>
  19. #include "common.hpp"
  20. #include "dataset.hpp"
  21. #include "Pcg32.hpp"
  22. #include "Cache.hpp"
  23. #include "virtualMemory.hpp"
  24. #if defined(__SSE2__)
  25. #include <wmmintrin.h>
  26. #define PREFETCH(memory) _mm_prefetch((const char *)((memory).ds.dataset + (memory).ma), _MM_HINT_NTA)
  27. #else
  28. #define PREFETCH(memory)
  29. #endif
  30. namespace RandomX {
  31. template<typename T>
  32. static inline void shuffle(T* buffer, size_t bytes, Pcg32& gen) {
  33. auto count = bytes / sizeof(T);
  34. for (auto i = count - 1; i >= 1; --i) {
  35. int j = gen.getUniform(0, i);
  36. std::swap(buffer[j], buffer[i]);
  37. }
  38. }
  39. template<bool soft>
  40. static inline __m128i aesenc(__m128i in, __m128i key) {
  41. return soft ? soft_aesenc(in, key) : _mm_aesenc_si128(in, key);
  42. }
  43. template<bool soft>
  44. static inline __m128i aesdec(__m128i in, __m128i key) {
  45. return soft ? soft_aesdec(in, key) : _mm_aesdec_si128(in, key);
  46. }
  47. #define AES_ROUND(i) x0 = aesdec<soft>(x0, keys[i]); \
  48. x1 = aesenc<soft>(x1, keys[i]); \
  49. x2 = aesdec<soft>(x2, keys[i]); \
  50. x3 = aesenc<soft>(x3, keys[i])
  51. template<bool soft>
  52. void initBlock(const uint8_t* intermediate, uint8_t* out, uint32_t blockNumber, const KeysContainer& keys) {
  53. __m128i x0, x1, x2, x3;
  54. __m128i* xit = (__m128i*)intermediate;
  55. __m128i* xout = (__m128i*)out;
  56. x0 = _mm_cvtsi32_si128(blockNumber);
  57. constexpr int mask = (CacheSize / CacheLineSize) - 1;
  58. for (auto i = 0; i < DatasetIterations; ++i) {
  59. x0 = aesenc<soft>(x0, keys[0]);
  60. x0 = aesenc<soft>(x0, keys[1]);
  61. x1 = aesenc<soft>(x0, keys[2]);
  62. x1 = aesenc<soft>(x1, keys[3]);
  63. x2 = aesenc<soft>(x1, keys[4]);
  64. x2 = aesenc<soft>(x2, keys[5]);
  65. x3 = aesenc<soft>(x2, keys[6]);
  66. x3 = aesenc<soft>(x3, keys[7]);
  67. int index = _mm_cvtsi128_si32(x3);
  68. index &= mask;
  69. __m128i t0 = _mm_load_si128(xit + 4 * index + 0);
  70. __m128i t1 = _mm_load_si128(xit + 4 * index + 1);
  71. __m128i t2 = _mm_load_si128(xit + 4 * index + 2);
  72. __m128i t3 = _mm_load_si128(xit + 4 * index + 3);
  73. x0 = _mm_xor_si128(x0, t0);
  74. x1 = _mm_xor_si128(x1, t1);
  75. x2 = _mm_xor_si128(x2, t2);
  76. x3 = _mm_xor_si128(x3, t3);
  77. }
  78. _mm_store_si128(xout + 0, x0);
  79. _mm_store_si128(xout + 1, x1);
  80. _mm_store_si128(xout + 2, x2);
  81. _mm_store_si128(xout + 3, x3);
  82. }
  83. template
  84. void initBlock<true>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  85. template
  86. void initBlock<false>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  87. void datasetRead(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
  88. uint64_t* datasetLine = (uint64_t*)(memory.ds.dataset + memory.ma);
  89. memory.mx ^= addr;
  90. memory.mx &= -64; //align to cache line
  91. std::swap(memory.mx, memory.ma);
  92. PREFETCH(memory);
  93. for (int i = 0; i < RegistersCount; ++i)
  94. reg.r[i].u64 ^= datasetLine[i];
  95. }
  96. template<bool softAes>
  97. void datasetReadLight(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
  98. Cache* cache = memory.ds.cache;
  99. uint64_t datasetLine[CacheLineSize / sizeof(uint64_t)];
  100. initBlock<softAes>(cache->getCache(), (uint8_t*)datasetLine, memory.ma / CacheLineSize, cache->getKeys());
  101. for (int i = 0; i < RegistersCount; ++i)
  102. reg.r[i].u64 ^= datasetLine[i];
  103. memory.mx ^= addr;
  104. memory.mx &= -64; //align to cache line
  105. std::swap(memory.mx, memory.ma);
  106. }
  107. template
  108. void datasetReadLight<false>(addr_t addr, MemoryRegisters& memory, RegisterFile& reg);
  109. template
  110. void datasetReadLight<true>(addr_t addr, MemoryRegisters& memory, RegisterFile& reg);
  111. void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
  112. ILightClientAsyncWorker* aw = memory.ds.asyncWorker;
  113. const uint64_t* datasetLine = aw->getBlock(memory.ma);
  114. for (int i = 0; i < RegistersCount; ++i)
  115. reg.r[i].u64 ^= datasetLine[i];
  116. memory.mx ^= addr;
  117. memory.mx &= -64; //align to cache line
  118. std::swap(memory.mx, memory.ma);
  119. aw->prepareBlock(memory.ma);
  120. }
  121. void datasetAlloc(dataset_t& ds, bool largePages) {
  122. if (sizeof(size_t) <= 4)
  123. throw std::runtime_error("Platform doesn't support enough memory for the dataset");
  124. if (largePages) {
  125. ds.dataset = (uint8_t*)allocLargePagesMemory(DatasetSize);
  126. }
  127. else {
  128. ds.dataset = (uint8_t*)_mm_malloc(DatasetSize, 64);
  129. if (ds.dataset == nullptr) {
  130. throw std::runtime_error("Dataset memory allocation failed. >4 GiB of free virtual memory is needed.");
  131. }
  132. }
  133. }
  134. template<bool softAes>
  135. void datasetInit(Cache* cache, dataset_t ds, uint32_t startBlock, uint32_t blockCount) {
  136. for (uint32_t i = startBlock; i < startBlock + blockCount; ++i) {
  137. initBlock<softAes>(cache->getCache(), ds.dataset + i * CacheLineSize, i, cache->getKeys());
  138. }
  139. }
  140. template
  141. void datasetInit<false>(Cache*, dataset_t, uint32_t, uint32_t);
  142. template
  143. void datasetInit<true>(Cache*, dataset_t, uint32_t, uint32_t);
  144. template<bool softAes>
  145. void datasetInitCache(const void* seed, dataset_t& ds, bool largePages) {
  146. ds.cache = new(Cache::alloc(largePages)) Cache();
  147. ds.cache->initialize<softAes>(seed, SeedSize);
  148. }
  149. template
  150. void datasetInitCache<false>(const void*, dataset_t&, bool);
  151. template
  152. void datasetInitCache<true>(const void*, dataset_t&, bool);
  153. template<bool softAes>
  154. void aesBench(uint32_t blockCount) {
  155. alignas(16) KeysContainer keys;
  156. alignas(16) uint8_t buffer[CacheLineSize];
  157. for (uint32_t block = 0; block < blockCount; ++block) {
  158. initBlock<softAes>(buffer, buffer, 0, keys);
  159. }
  160. }
  161. template void aesBench<false>(uint32_t blockCount);
  162. template void aesBench<true>(uint32_t blockCount);
  163. }