Skip to content

Commit 7bece33

Browse files
authored
Merge pull request #2579 from subspace/farmer-caching-tweaks
Farmer caching tweaks
2 parents a986e2a + df919f9 commit 7bece33

File tree

4 files changed

+47
-24
lines changed

4 files changed

+47
-24
lines changed

crates/subspace-farmer-components/src/file_ext.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ impl OpenOptionsExt for OpenOptions {
3030
#[cfg(windows)]
3131
fn advise_random_access(&mut self) -> &mut Self {
3232
use std::os::windows::fs::OpenOptionsExt;
33-
self.custom_flags(winapi::um::winbase::FILE_FLAG_RANDOM_ACCESS)
33+
// `FILE_FLAG_WRITE_THROUGH` below is a bit of a hack, especially in `advise_random_access`,
34+
// but it helps with memory usage and feels like should be default. Since `.custom_flags()`
35+
// overrides previous value, we need to set bitwise OR of two flags rather that two flags
36+
// separately.
37+
self.custom_flags(
38+
winapi::um::winbase::FILE_FLAG_RANDOM_ACCESS
39+
| winapi::um::winbase::FILE_FLAG_WRITE_THROUGH,
40+
)
3441
}
3542

3643
#[cfg(target_os = "linux")]

crates/subspace-farmer/src/farmer_cache.rs

+8
Original file line numberDiff line numberDiff line change
@@ -886,10 +886,18 @@ impl FarmerCache {
886886

887887
let should_store_fut = tokio::task::spawn_blocking({
888888
let plot_caches = Arc::clone(&self.plot_caches);
889+
let piece_caches = Arc::clone(&self.piece_caches);
889890
let next_plot_cache = Arc::clone(&self.next_plot_cache);
890891
let piece = piece.clone();
891892

892893
move || {
894+
for cache in piece_caches.read().iter() {
895+
if cache.stored_pieces.contains_key(&key) {
896+
// Already stored in normal piece cache, no need to store it again
897+
return;
898+
}
899+
}
900+
893901
let plot_caches = plot_caches.read();
894902
let plot_caches_len = plot_caches.len();
895903

crates/subspace-farmer/src/single_disk_farm/plot_cache.rs

+30-20
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ use subspace_networking::utils::multihash::ToMultihash;
1616
use thiserror::Error;
1717
use tracing::{debug, info, warn};
1818

19+
/// Max plot space for which to use caching, for larger gaps between the plotted part and the end of
20+
/// the file it will result in very long period of writing zeroes on Windows, see
21+
/// https://stackoverflow.com/q/78058306/3806795
22+
///
23+
/// Currently set to 2TiB.
24+
const MAX_WINDOWS_PLOT_SPACE_FOR_CACHE: u64 = 2 * 1024 * 1024 * 1024 * 1024;
25+
1926
/// Disk plot cache open error
2027
#[derive(Debug, Error)]
2128
pub enum DiskPlotCacheError {
@@ -68,33 +75,36 @@ impl DiskPlotCache {
6875
// Clippy complains about `RecordKey`, but it is not changing here, so it is fine
6976
#[allow(clippy::mutable_key_type)]
7077
let mut map = HashMap::new();
78+
let mut next_offset = None;
7179

7280
let file_size = sector_size * u64::from(target_sector_count);
7381
let plotted_size = sector_size * sectors_metadata.len() as u64;
7482

75-
// Step over all free potential offsets for pieces that could have been cached
76-
let from_offset = (plotted_size / Self::element_size() as u64) as u32;
77-
let to_offset = (file_size / Self::element_size() as u64) as u32;
78-
let mut next_offset = None;
79-
// TODO: Parallelize or read in larger batches
80-
for offset in (from_offset..to_offset).rev() {
81-
match Self::read_piece_internal(file, offset, &mut element) {
82-
Ok(maybe_piece_index) => match maybe_piece_index {
83-
Some(piece_index) => {
84-
map.insert(RecordKey::from(piece_index.to_multihash()), offset);
85-
}
86-
None => {
83+
// Avoid writing over large gaps on Windows that is very lengthy process
84+
if !cfg!(windows) || (file_size - plotted_size) <= MAX_WINDOWS_PLOT_SPACE_FOR_CACHE {
85+
// Step over all free potential offsets for pieces that could have been cached
86+
let from_offset = (plotted_size / Self::element_size() as u64) as u32;
87+
let to_offset = (file_size / Self::element_size() as u64) as u32;
88+
// TODO: Parallelize or read in larger batches
89+
for offset in (from_offset..to_offset).rev() {
90+
match Self::read_piece_internal(file, offset, &mut element) {
91+
Ok(maybe_piece_index) => match maybe_piece_index {
92+
Some(piece_index) => {
93+
map.insert(RecordKey::from(piece_index.to_multihash()), offset);
94+
}
95+
None => {
96+
next_offset.replace(offset);
97+
break;
98+
}
99+
},
100+
Err(DiskPlotCacheError::ChecksumMismatch) => {
87101
next_offset.replace(offset);
88102
break;
89103
}
90-
},
91-
Err(DiskPlotCacheError::ChecksumMismatch) => {
92-
next_offset.replace(offset);
93-
break;
94-
}
95-
Err(error) => {
96-
warn!(%error, %offset, "Failed to read plot cache element");
97-
break;
104+
Err(error) => {
105+
warn!(%error, %offset, "Failed to read plot cache element");
106+
break;
107+
}
98108
}
99109
}
100110
}

crates/subspace-farmer/src/utils.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ use std::{io, thread};
2020
use thread_priority::{set_current_thread_priority, ThreadPriority};
2121
use tokio::runtime::Handle;
2222
use tokio::task;
23-
use tracing::debug;
24-
#[cfg(feature = "numa")]
25-
use tracing::warn;
23+
use tracing::{debug, warn};
2624

2725
/// It doesn't make a lot of sense to have a huge number of farming threads, 32 is plenty
2826
const MAX_DEFAULT_FARMING_THREADS: usize = 32;

0 commit comments

Comments
 (0)