Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rafs: prefetch based on blob chunks rather than files #887

Merged
merged 1 commit into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 77 additions & 27 deletions rafs/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ use serde::Deserialize;

use nydus_api::http::{BlobPrefetchConfig, FactoryConfig};
use nydus_storage::device::{BlobDevice, BlobPrefetchRequest};
use nydus_utils::metrics::{self, FopRecorder, StatsFop::*};
use nydus_utils::{
div_round_up,
metrics::{self, FopRecorder, StatsFop::*},
};
use storage::RAFS_DEFAULT_CHUNK_SIZE;

use crate::metadata::{
Expand Down Expand Up @@ -505,10 +508,10 @@ impl Rafs {
device: BlobDevice,
) {
// First do range based prefetch for rafs v6.
if sb.meta.is_v6() {
let blob_infos = sb.superblock.get_blob_infos();
if sb.meta.is_v6() && !blob_infos.is_empty() {
let mut prefetches = Vec::new();

for blob in sb.superblock.get_blob_infos() {
for blob in &blob_infos {
let sz = blob.readahead_size();
if sz > 0 {
let mut offset = 0;
Expand All @@ -530,40 +533,87 @@ impl Rafs {
}
}

let mut ignore_prefetch_all = prefetch_files
// Bootstrap has non-empty prefetch table indicating a full prefetch
let inlay_prefetch_all = sb
.is_inlay_prefetch_all(&mut reader)
.map_err(|e| error!("Detect prefetch table error {}", e))
.unwrap_or_default();

// Nydusd has a CLI option indicating a full prefetch
let startup_prefetch_all = prefetch_files
.as_ref()
.map(|f| f.len() == 1 && f[0].as_os_str() == "/")
.unwrap_or(false);

// Then do file based prefetch based on:
// - prefetch listed passed in by user
// - or file prefetch list in metadata
let inodes = prefetch_files.map(|files| Self::convert_file_list(&files, &sb));
let res = sb.prefetch_files(&mut reader, root_ino, inodes, &|desc| {
if desc.bi_size > 0 {
device.prefetch(&[desc], &[]).unwrap_or_else(|e| {
warn!("Prefetch error, {:?}", e);
});
}
});
match res {
Ok(true) => ignore_prefetch_all = true,
Ok(false) => {}
Err(e) => info!("No file to be prefetched {:?}", e),
}
let mut ignore_prefetch_all = false;

// Last optionally prefetch all data
if prefetch_all && !ignore_prefetch_all {
let root = vec![root_ino];
let res = sb.prefetch_files(&mut reader, root_ino, Some(root), &|desc| {
// User specified prefetch files have high priority to be prefetched.
// Moreover, user specified prefetch files list will override those on-disk prefetch table.
if !startup_prefetch_all && !inlay_prefetch_all {
// Then do file based prefetch based on:
// - prefetch listed passed in by user
// - or file prefetch list in metadata
let inodes = prefetch_files.map(|files| Self::convert_file_list(&files, &sb));
let res = sb.prefetch_files(&mut reader, root_ino, inodes, &|desc| {
if desc.bi_size > 0 {
device.prefetch(&[desc], &[]).unwrap_or_else(|e| {
warn!("Prefetch error, {:?}", e);
});
}
});
if let Err(e) = res {
info!("No file to be prefetched {:?}", e);
match res {
Ok(true) => {
ignore_prefetch_all = true;
warn!("Root inode was found, but it should not prefetch all files!")
}
Ok(false) => {}
Err(e) => info!("No file to be prefetched {:?}", e),
}
}

// Perform different policy for v5 format and v6 format as rafs v6's blobs are capable to
// to download chunks and decompress them all by themselves. For rafs v6, directly perform
// chunk based full prefetch
if !ignore_prefetch_all && (inlay_prefetch_all || prefetch_all || startup_prefetch_all) {
if sb.meta.is_v6() {
// The larger batch size, the fewer requests to registry
let batch_size = 1024 * 1024 * 2;

for blob in &blob_infos {
let blob_size = blob.compressed_size();
let count = div_round_up(blob_size, batch_size);

let mut pre_offset = 0u64;

for _i in 0..count {
let req = BlobPrefetchRequest {
blob_id: blob.blob_id().to_owned(),
offset: pre_offset,
len: cmp::min(batch_size, blob_size - pre_offset),
};
pre_offset += batch_size;
if pre_offset > blob_size {
break;
}

device
.prefetch(&[], &[req])
.map_err(|e| warn!("failed to prefetch blob data, {}", e))
.unwrap_or_default();
}
}
} else {
let root = vec![root_ino];
let res = sb.prefetch_files(&mut reader, root_ino, Some(root), &|desc| {
if desc.bi_size > 0 {
device.prefetch(&[desc], &[]).unwrap_or_else(|e| {
warn!("Prefetch error, {:?}", e);
});
}
});
if let Err(e) = res {
info!("No file to be prefetched {:?}", e);
}
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions rafs/src/metadata/md_v6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,38 @@ impl RafsSuper {
}
}

pub(crate) fn is_inlay_prefetch_all(&self, r: &mut RafsIoReader) -> RafsResult<bool> {
changweige marked this conversation as resolved.
Show resolved Hide resolved
let hint_entries = self.meta.prefetch_table_entries as usize;
if hint_entries != 1 {
return Ok(false);
}
let unique = if self.meta.is_v6() {
let mut prefetch_table = RafsV6PrefetchTable::new();
prefetch_table
.load_prefetch_table_from(r, self.meta.prefetch_table_offset, hint_entries)
.map_err(|e| {
RafsError::Prefetch(format!(
"Failed in loading hint prefetch table at offset {}. {:?}",
self.meta.prefetch_table_offset, e
))
})?;
prefetch_table.inodes[0] as u64
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

md_v6.rs should only contains code for v6, so is this branch redundant?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like it's used for both v5 and v6, so it would be better to move into a common source file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Could we do the code movement on the master branch as I am planning to commit these changes to the master in the future?

let mut prefetch_table = RafsV5PrefetchTable::new();
prefetch_table
.load_prefetch_table_from(r, self.meta.prefetch_table_offset, hint_entries)
.map_err(|e| {
RafsError::Prefetch(format!(
"Failed in loading hint prefetch table at offset {}. {:?}",
self.meta.prefetch_table_offset, e
))
})?;
prefetch_table.inodes[0] as u64
};

Ok(unique == self.superblock.root_ino())
}

pub(crate) fn prefetch_data_v6<F>(
&self,
r: &mut RafsIoReader,
Expand Down
1 change: 1 addition & 0 deletions rafs/src/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ impl RafsSuper {
///
/// Each inode passed into should correspond to directory. And it already does the file type
/// check inside.
/// Return Ok(true) means root inode is found during performing prefetching and all files should be prefetched.
pub fn prefetch_files(
&self,
r: &mut RafsIoReader,
Expand Down