205 lines
5.9 KiB
Rust
205 lines
5.9 KiB
Rust
// 文件创建和清理位于计时之外;同一文件先预热,不把页缓存当作编译器收益。
|
|
struct ResourceCase {
|
|
path: String,
|
|
limit: i64,
|
|
}
|
|
|
|
struct ResourceDirectory(std::path::PathBuf);
|
|
|
|
impl Drop for ResourceDirectory {
|
|
fn drop(&mut self) {
|
|
fs::remove_dir_all(&self.0).unwrap();
|
|
}
|
|
}
|
|
|
|
fn resource_fault(_: impl std::fmt::Display) -> Fault {
|
|
&FaultInfo {
|
|
source: "resource-reference",
|
|
line: 1,
|
|
column: 1,
|
|
message: "resource failure",
|
|
}
|
|
}
|
|
|
|
fn generated_open(values: &[ResourceCase]) -> Result<bool, Fault> {
|
|
values.first().map_or(Ok(true), |value| {
|
|
GENERATED_OPEN(&value.path).map_err(resource_fault)
|
|
})
|
|
}
|
|
|
|
fn reference_open(values: &[ResourceCase]) -> Result<bool, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(true);
|
|
};
|
|
let file = ReferenceFile::open(&value.path).map_err(resource_fault)?;
|
|
let alias = file.clone();
|
|
alias.close().map_err(resource_fault)?;
|
|
Ok(true)
|
|
}
|
|
|
|
fn generated_read(values: &[ResourceCase]) -> Result<Vec<u8>, Fault> {
|
|
values.first().map_or(Ok(Vec::new()), |value| {
|
|
GENERATED_READ(&value.path, value.limit).map_err(resource_fault)
|
|
})
|
|
}
|
|
|
|
fn reference_read(values: &[ResourceCase]) -> Result<Vec<u8>, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(Vec::new());
|
|
};
|
|
let file = ReferenceFile::open(&value.path).map_err(resource_fault)?;
|
|
let bytes = file
|
|
.read(value.limit)
|
|
.map_err(resource_fault)?
|
|
.unwrap_or_default();
|
|
file.close().map_err(resource_fault)?;
|
|
Ok(bytes)
|
|
}
|
|
|
|
fn generated_stream(values: &[ResourceCase]) -> Result<i64, Fault> {
|
|
values.first().map_or(Ok(0), |value| {
|
|
GENERATED_STREAM(&value.path, value.limit).map_err(resource_fault)
|
|
})
|
|
}
|
|
|
|
fn reference_stream(values: &[ResourceCase]) -> Result<i64, Fault> {
|
|
values.first().map_or(Ok(0), |value| {
|
|
reference_consume(&value.path, value.limit, false).map_err(resource_fault)
|
|
})
|
|
}
|
|
|
|
fn generated_partial(values: &[ResourceCase]) -> Result<i64, Fault> {
|
|
values.first().map_or(Ok(0), |value| {
|
|
GENERATED_PARTIAL(&value.path, value.limit).map_err(resource_fault)
|
|
})
|
|
}
|
|
|
|
fn reference_partial(values: &[ResourceCase]) -> Result<i64, Fault> {
|
|
values.first().map_or(Ok(0), |value| {
|
|
reference_consume(&value.path, value.limit, true).map_err(resource_fault)
|
|
})
|
|
}
|
|
|
|
fn generated_closed(values: &[ResourceCase]) -> Result<bool, Fault> {
|
|
Ok(values
|
|
.first()
|
|
.is_none_or(|value| GENERATED_CLOSED(&value.path).is_err()))
|
|
}
|
|
|
|
fn reference_closed(values: &[ResourceCase]) -> Result<bool, Fault> {
|
|
let Some(value) = values.first() else {
|
|
return Ok(true);
|
|
};
|
|
let file = ReferenceFile::open(&value.path).map_err(resource_fault)?;
|
|
let stream = ReferenceStream::new(&file, 64).map_err(resource_fault)?;
|
|
file.close().map_err(resource_fault)?;
|
|
Ok(stream.next().unwrap().is_err())
|
|
}
|
|
|
|
fn main() {
|
|
let path =
|
|
std::env::temp_dir().join(format!("dever-resource-performance-{}", std::process::id()));
|
|
fs::create_dir(&path).unwrap();
|
|
let directory = ResourceDirectory(path);
|
|
let path = directory.0.join("input");
|
|
let bytes = (0..65537)
|
|
.map(|index| (index % 251) as u8)
|
|
.collect::<Vec<_>>();
|
|
fs::write(&path, &bytes).unwrap();
|
|
let path = path.to_str().unwrap().to_owned();
|
|
assert_eq!(GENERATED_READ(&path, 1_000_000).unwrap(), bytes);
|
|
for limit in [0, -1, i64::MAX] {
|
|
assert!(GENERATED_READ(&path, limit).is_err());
|
|
assert!(ReferenceFile::open(&path).unwrap().read(limit).is_err());
|
|
}
|
|
let expected: i64 = bytes.iter().map(|byte| i64::from(*byte)).sum();
|
|
for limit in [64, 4096] {
|
|
assert_eq!(GENERATED_STREAM(&path, limit).unwrap(), expected);
|
|
assert_eq!(GENERATED_PARTIAL(&path, limit).unwrap(), expected);
|
|
}
|
|
let closed = GENERATED_CLOSED(&path).unwrap_err();
|
|
assert!(
|
|
closed.to_string().contains("ReadEvent.Failed")
|
|
&& format!("{closed:?}").contains("file is closed"),
|
|
"{closed}"
|
|
);
|
|
let file = ReferenceFile::open(&path).unwrap();
|
|
let alias = file.clone();
|
|
assert_eq!(file.read(2).unwrap().unwrap(), bytes[..2]);
|
|
assert_eq!(alias.read(2).unwrap().unwrap(), bytes[2..4]);
|
|
let stream = ReferenceStream::new(&file, 2).unwrap();
|
|
alias.close().unwrap();
|
|
assert!(stream.next().unwrap().is_err());
|
|
assert!(stream.next().is_none());
|
|
println!("case,sample,implementation,elapsed_ns,checksum");
|
|
let values = [ResourceCase {
|
|
path: path.clone(),
|
|
limit: 64,
|
|
}];
|
|
compare(
|
|
"file_open_close",
|
|
&values,
|
|
2000,
|
|
generated_open,
|
|
reference_open,
|
|
|value| u64::from(*value),
|
|
);
|
|
compare(
|
|
"file_read_64",
|
|
&values,
|
|
2000,
|
|
generated_read,
|
|
reference_read,
|
|
|value| value.len() as u64,
|
|
);
|
|
compare(
|
|
"stream_read_64",
|
|
&values,
|
|
50,
|
|
generated_stream,
|
|
reference_stream,
|
|
|value| *value as u64,
|
|
);
|
|
compare(
|
|
"stream_resume_64",
|
|
&values,
|
|
50,
|
|
generated_partial,
|
|
reference_partial,
|
|
|value| *value as u64,
|
|
);
|
|
compare(
|
|
"stream_closed",
|
|
&values,
|
|
2000,
|
|
generated_closed,
|
|
reference_closed,
|
|
|value| u64::from(*value),
|
|
);
|
|
let values = [ResourceCase { path, limit: 4096 }];
|
|
compare(
|
|
"file_read_4096",
|
|
&values,
|
|
1000,
|
|
generated_read,
|
|
reference_read,
|
|
|value| value.len() as u64,
|
|
);
|
|
compare(
|
|
"stream_read_4096",
|
|
&values,
|
|
200,
|
|
generated_stream,
|
|
reference_stream,
|
|
|value| *value as u64,
|
|
);
|
|
compare(
|
|
"stream_resume_4096",
|
|
&values,
|
|
200,
|
|
generated_partial,
|
|
reference_partial,
|
|
|value| *value as u64,
|
|
);
|
|
}
|