dataset.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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* in, uint8_t* out, uint32_t blockNumber, const KeysContainer& keys) {
  53. __m128i x0, x1, x2, x3, iv;
  54. //block number 0..67108863
  55. //Initialization vector = block number extended to 128 bits
  56. iv = _mm_cvtsi32_si128(blockNumber);
  57. uint32_t cacheBlockNumber = blockNumber / BlockExpansionRatio; //0..2097151
  58. __m128i* cacheCacheLine = (__m128i*)(in + cacheBlockNumber * CacheLineSize);
  59. __m128i* datasetCacheLine = (__m128i*)out;
  60. x0 = _mm_load_si128(cacheCacheLine + 0);
  61. x1 = _mm_load_si128(cacheCacheLine + 1);
  62. x2 = _mm_load_si128(cacheCacheLine + 2);
  63. x3 = _mm_load_si128(cacheCacheLine + 3);
  64. x0 = _mm_xor_si128(x0, iv);
  65. x1 = _mm_xor_si128(x1, iv);
  66. x2 = _mm_xor_si128(x2, iv);
  67. x3 = _mm_xor_si128(x3, iv);
  68. for (auto i = 0; i < DatasetIterations; ++i) {
  69. AES_ROUND(0);
  70. AES_ROUND(1);
  71. AES_ROUND(2);
  72. AES_ROUND(3);
  73. AES_ROUND(4);
  74. AES_ROUND(5);
  75. AES_ROUND(6);
  76. AES_ROUND(7);
  77. AES_ROUND(8);
  78. AES_ROUND(9);
  79. }
  80. _mm_store_si128(datasetCacheLine + 0, x0);
  81. _mm_store_si128(datasetCacheLine + 1, x1);
  82. _mm_store_si128(datasetCacheLine + 2, x2);
  83. _mm_store_si128(datasetCacheLine + 3, x3);
  84. }
  85. template
  86. void initBlock<true>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  87. template
  88. void initBlock<false>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  89. void datasetRead(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
  90. uint64_t* datasetLine = (uint64_t*)(memory.ds.dataset + memory.ma);
  91. memory.mx ^= addr;
  92. memory.mx &= -64; //align to cache line
  93. std::swap(memory.mx, memory.ma);
  94. PREFETCH(memory);
  95. for (int i = 0; i < RegistersCount; ++i)
  96. reg.r[i].u64 ^= datasetLine[i];
  97. }
  98. template<bool softAes>
  99. void datasetReadLight(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
  100. Cache* cache = memory.ds.cache;
  101. uint64_t datasetLine[CacheLineSize / sizeof(uint64_t)];
  102. initBlock<softAes>(cache->getCache(), (uint8_t*)datasetLine, memory.ma / CacheLineSize, cache->getKeys());
  103. for (int i = 0; i < RegistersCount; ++i)
  104. reg.r[i].u64 ^= datasetLine[i];
  105. memory.mx ^= addr;
  106. memory.mx &= -64; //align to cache line
  107. std::swap(memory.mx, memory.ma);
  108. }
  109. template
  110. void datasetReadLight<false>(addr_t addr, MemoryRegisters& memory, RegisterFile& reg);
  111. template
  112. void datasetReadLight<true>(addr_t addr, MemoryRegisters& memory, RegisterFile& reg);
  113. void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
  114. ILightClientAsyncWorker* aw = memory.ds.asyncWorker;
  115. const uint64_t* datasetLine = aw->getBlock(memory.ma);
  116. for (int i = 0; i < RegistersCount; ++i)
  117. reg.r[i].u64 ^= datasetLine[i];
  118. memory.mx ^= addr;
  119. memory.mx &= -64; //align to cache line
  120. std::swap(memory.mx, memory.ma);
  121. aw->prepareBlock(memory.ma);
  122. }
  123. void datasetAlloc(dataset_t& ds, bool largePages) {
  124. if (sizeof(size_t) <= 4)
  125. throw std::runtime_error("Platform doesn't support enough memory for the dataset");
  126. if (largePages) {
  127. ds.dataset = (uint8_t*)allocLargePagesMemory(DatasetSize);
  128. }
  129. else {
  130. ds.dataset = (uint8_t*)_mm_malloc(DatasetSize, 64);
  131. if (ds.dataset == nullptr) {
  132. throw std::runtime_error("Dataset memory allocation failed. >4 GiB of free virtual memory is needed.");
  133. }
  134. }
  135. }
  136. template<bool softAes>
  137. void datasetInit(Cache* cache, dataset_t ds, uint32_t startBlock, uint32_t blockCount) {
  138. for (uint32_t i = startBlock; i < startBlock + blockCount; ++i) {
  139. initBlock<softAes>(cache->getCache(), ds.dataset + i * CacheLineSize, i, cache->getKeys());
  140. }
  141. }
  142. template
  143. void datasetInit<false>(Cache*, dataset_t, uint32_t, uint32_t);
  144. template
  145. void datasetInit<true>(Cache*, dataset_t, uint32_t, uint32_t);
  146. template<bool softAes>
  147. void datasetInitCache(const void* seed, dataset_t& ds, bool largePages) {
  148. ds.cache = new(Cache::alloc(largePages)) Cache();
  149. ds.cache->initialize<softAes>(seed, SeedSize);
  150. }
  151. template
  152. void datasetInitCache<false>(const void*, dataset_t&, bool);
  153. template
  154. void datasetInitCache<true>(const void*, dataset_t&, bool);
  155. template<bool softAes>
  156. void aesBench(uint32_t blockCount) {
  157. alignas(16) KeysContainer keys;
  158. alignas(16) uint8_t buffer[CacheLineSize];
  159. for (uint32_t block = 0; block < blockCount; ++block) {
  160. initBlock<softAes>(buffer, buffer, 0, keys);
  161. }
  162. }
  163. template void aesBench<false>(uint32_t blockCount);
  164. template void aesBench<true>(uint32_t blockCount);
  165. }