multithreaded_mining.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. //! randomx example that mines a dummy header using multiple threads
  2. use std::{
  3. collections::VecDeque,
  4. sync::{
  5. atomic::{AtomicBool, AtomicU64, Ordering},
  6. Arc,
  7. },
  8. thread,
  9. time::Instant,
  10. };
  11. use num_bigint::BigUint;
  12. use randomx::*;
  13. #[derive(Clone, Debug)]
  14. struct Header {
  15. version: u8,
  16. previous: blake3::Hash,
  17. height: u32,
  18. nonce: u64,
  19. }
  20. impl Header {
  21. fn new(previous: blake3::Hash) -> Self {
  22. Self {
  23. version: 1,
  24. previous,
  25. height: 1,
  26. nonce: 0,
  27. }
  28. }
  29. fn hash(&self) -> blake3::Hash {
  30. let mut hasher = blake3::Hasher::new();
  31. hasher.update(&self.version.to_le_bytes());
  32. hasher.update(self.previous.as_bytes());
  33. hasher.update(&self.height.to_le_bytes());
  34. hasher.update(&self.nonce.to_le_bytes());
  35. hasher.finalize()
  36. }
  37. }
  38. fn get_mining_flags() -> RandomXFlags {
  39. // Try adding `| RandomXFlags::LARGEPAGES`.
  40. let mut flags = RandomXFlags::get_recommended_flags() | RandomXFlags::FULLMEM;
  41. if is_x86_feature_detected!("avx2") {
  42. flags |= RandomXFlags::ARGON2_AVX2;
  43. } else if is_x86_feature_detected!("ssse3") {
  44. flags |= RandomXFlags::ARGON2_SSSE3;
  45. }
  46. flags
  47. }
  48. fn single_thread_mine(header: &mut Header, target: &BigUint, input: &[u8; 32]) {
  49. println!("Initializing RandomX cache and dataset...");
  50. let setup_start = Instant::now();
  51. let flags = get_mining_flags();
  52. let cache = RandomXCache::new(flags, &input[..]).unwrap();
  53. let dataset_item_count = RandomXDataset::count().unwrap();
  54. let dataset = RandomXDataset::new_init(flags, cache, 0, dataset_item_count).unwrap();
  55. println!("Setup time: {:?}", setup_start.elapsed());
  56. println!("Initializing RandomX VM...");
  57. let vm_start = Instant::now();
  58. let vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
  59. println!("Initialized RandomX VM in {:?}", vm_start.elapsed());
  60. println!("Mining started!");
  61. let mining_start = Instant::now();
  62. loop {
  63. let out_hash = vm.calculate_hash(header.hash().as_bytes()).unwrap();
  64. let out_hash = BigUint::from_bytes_le(&out_hash);
  65. if &out_hash <= target {
  66. println!("Found block header using nonce {}", header.nonce);
  67. println!("Block header hash {}", header.hash());
  68. println!("RandomX output: 0x{out_hash:064x}");
  69. break;
  70. }
  71. header.nonce += 1;
  72. }
  73. println!("Completed mining in {:?}", mining_start.elapsed());
  74. println!("Mined header: {header:?}");
  75. }
  76. fn multi_thread_mine(threads: usize, header: &mut Header, target: &BigUint, input: &[u8; 32]) {
  77. println!("Initializing RandomX cache and dataset...");
  78. let setup_start = Instant::now();
  79. let flags = get_mining_flags();
  80. let cache = RandomXCache::new(flags, &input[..]).unwrap();
  81. let dataset_item_count = RandomXDataset::count().unwrap();
  82. let dataset = RandomXDataset::new(flags, cache, dataset_item_count).unwrap();
  83. let mut subsets = VecDeque::with_capacity(threads);
  84. // Multithreaded dataset init
  85. let threads_u32 = threads as u32;
  86. for t in 0..threads_u32 {
  87. println!("Initializing RandomX dataset for thread #{t}...");
  88. let ds_start = Instant::now();
  89. let a = (dataset_item_count * t) / threads_u32;
  90. let b = (dataset_item_count * (t + 1)) / threads_u32;
  91. subsets.push_back(dataset.subset_init(a, b - a));
  92. println!(
  93. "Initialized RandomX dataset for thread #{t} in {:?}",
  94. ds_start.elapsed()
  95. );
  96. }
  97. println!("Setup time: {:?}", setup_start.elapsed());
  98. println!("Initializing mining threads...");
  99. let mut handles = Vec::with_capacity(threads);
  100. let found_header = Arc::new(AtomicBool::new(false));
  101. let found_nonce = Arc::new(AtomicU64::new(0));
  102. let threads_u64 = threads as u64;
  103. let mining_start = Instant::now();
  104. for t in 0..threads_u64 {
  105. if found_header.load(Ordering::SeqCst) {
  106. println!("Block header found, threads creation loop exiting");
  107. break;
  108. }
  109. let target = target.clone();
  110. let mut thread_header = header.clone();
  111. thread_header.nonce = t;
  112. let found_header = Arc::clone(&found_header);
  113. let found_nonce = Arc::clone(&found_nonce);
  114. let dataset = subsets.pop_front().unwrap();
  115. handles.push(thread::spawn(move || {
  116. println!("Initializing RandomX VM #{t}...");
  117. let vm_start = Instant::now();
  118. let vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
  119. println!("Initialized RandomX VM #{t} in {:?}", vm_start.elapsed());
  120. loop {
  121. if found_header.load(Ordering::SeqCst) {
  122. println!("Block header found, thread #{t} exiting");
  123. break;
  124. }
  125. let out_hash = vm.calculate_hash(thread_header.hash().as_bytes()).unwrap();
  126. let out_hash = BigUint::from_bytes_le(&out_hash);
  127. if out_hash <= target {
  128. found_header.store(true, Ordering::SeqCst);
  129. found_nonce.store(thread_header.nonce, Ordering::SeqCst);
  130. println!(
  131. "Thread #{t} found block header using nonce {}",
  132. thread_header.nonce
  133. );
  134. println!("Block header hash {}", thread_header.hash());
  135. println!("RandomX output: 0x{out_hash:064x}");
  136. break;
  137. }
  138. // This means thread 0 will use nonces, 0, 4, 8, ...
  139. // and thread 1 will use nonces, 1, 5, 9, ...
  140. thread_header.nonce += threads_u64;
  141. }
  142. }));
  143. }
  144. // Wait for threads to finish mining
  145. for handle in handles {
  146. let _ = handle.join();
  147. }
  148. println!("Completed mining in {:?}", mining_start.elapsed());
  149. header.nonce = found_nonce.load(Ordering::SeqCst);
  150. println!("Mined header: {header:?}");
  151. }
  152. fn main() {
  153. const THREADS: usize = 8;
  154. let difficulty = BigUint::from(2500_u32);
  155. let target = BigUint::from_bytes_le(&[0xFF; 32]) / difficulty;
  156. let previous = blake3::hash(b"Let there be dark!");
  157. let input = previous.as_bytes();
  158. let mut header = Header::new(previous);
  159. println!("Mine target: 0x{target:064x}");
  160. println!("PoW input: {previous}");
  161. match THREADS {
  162. 0 => panic!("Can't use 0 threads!"),
  163. 1 => single_thread_mine(&mut header, &target, input),
  164. _ => multi_thread_mine(THREADS, &mut header, &target, input),
  165. }
  166. println!("Initializing verifier...");
  167. let verifier_setup = Instant::now();
  168. let flags = RandomXFlags::get_recommended_flags();
  169. let cache = RandomXCache::new(flags, input).unwrap();
  170. let vm = RandomXVM::new(flags, Some(cache), None).unwrap();
  171. println!("Setup time: {:?}", verifier_setup.elapsed());
  172. let hashing_time = Instant::now();
  173. let out_hash = vm.calculate_hash(header.hash().as_bytes()).unwrap();
  174. println!("Completed hashing in {:?}", hashing_time.elapsed());
  175. let out_hash = BigUint::from_bytes_le(&out_hash);
  176. println!("RandomX output: 0x{out_hash:064x}");
  177. assert!(out_hash <= target);
  178. }