multithreaded_mining.rs 7.7 KB

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