Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,277 @@
|
||||
// 两侧共享 SQLite、连接池、截止时间与 worker,单独测量 Dever 生成层的额外成本。
|
||||
// 不把此比较当成未经资源约束的同步 rusqlite、HTTP 吞吐或整个 core 的验收。
|
||||
use dever_runtime::{
|
||||
database::{Context, Id as ModelId, Parameter, Registry},
|
||||
task::{Limits, Runtime, Scope},
|
||||
time::calendar::DateTime,
|
||||
value::uuid::Uuid,
|
||||
};
|
||||
use std::{hint::black_box, path::PathBuf, sync::Arc, time::Instant};
|
||||
|
||||
struct Directory(PathBuf);
|
||||
impl Drop for Directory {
|
||||
fn drop(&mut self) {
|
||||
std::fs::remove_dir_all(&self.0).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
async fn rust_count(context: &mut Context, scope: &Scope<'_>) -> i64 {
|
||||
context
|
||||
.on(0, scope)
|
||||
.await
|
||||
.unwrap()
|
||||
.query(
|
||||
scope,
|
||||
"SELECT COUNT(*) FROM \"demo_account_model\"",
|
||||
vec![],
|
||||
1,
|
||||
|row| Ok(row.get::<_, i64>(0)?),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
struct RustAccount {
|
||||
id: ModelId<0>,
|
||||
created_at: DateTime,
|
||||
name: String,
|
||||
note: Option<String>,
|
||||
uid: Uuid,
|
||||
rank: i64,
|
||||
}
|
||||
|
||||
async fn rust_row(
|
||||
context: &mut Context,
|
||||
scope: &Scope<'_>,
|
||||
update: bool,
|
||||
) -> Result<RustAccount, dever_runtime::database::Error> {
|
||||
let (sql, parameters) = if update {
|
||||
(
|
||||
"UPDATE \"demo_account_model\" SET \"name\" = ?2 WHERE id = ?1 RETURNING \"id\",\"created_at\",\"name\",\"note\",\"uid\",\"rank\"",
|
||||
vec![
|
||||
Parameter::Integer(1),
|
||||
Parameter::Text("benchmark 中文".into()),
|
||||
],
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"SELECT \"id\",\"created_at\",\"name\",\"note\",\"uid\",\"rank\" FROM \"demo_account_model\" WHERE id = ?1",
|
||||
vec![Parameter::Integer(1)],
|
||||
)
|
||||
};
|
||||
let sql = dever_runtime::database::Sql {
|
||||
sqlite: sql,
|
||||
postgres: if update {
|
||||
"UPDATE \"demo_account_model\" SET \"name\" = $2 WHERE id = $1 RETURNING \"id\",\"created_at\",\"name\",\"note\",\"uid\",\"rank\""
|
||||
} else {
|
||||
"SELECT \"id\",\"created_at\",\"name\",\"note\",\"uid\",\"rank\" FROM \"demo_account_model\" WHERE id = $1"
|
||||
},
|
||||
};
|
||||
context
|
||||
.on(0, scope)
|
||||
.await?
|
||||
.query(scope, sql, parameters, 1, |row| {
|
||||
Ok(RustAccount {
|
||||
id: ModelId(row.get::<_, i64>(0)?),
|
||||
created_at: DateTime::from_millis(row.get::<_, i64>(1)?).map_err(|_| {
|
||||
dever_runtime::database::Error::new(
|
||||
dever_runtime::database::Kind::InvalidData,
|
||||
"invalid stored DateTime",
|
||||
)
|
||||
})?,
|
||||
name: row.get::<_, String>(2)?,
|
||||
note: row.get::<_, Option<String>>(3)?,
|
||||
uid: dever_runtime::database::scalar::uuid(row, 4)?,
|
||||
rank: row.get::<_, i64>(5)?,
|
||||
})
|
||||
})
|
||||
.await?
|
||||
.pop()
|
||||
.ok_or_else(|| {
|
||||
dever_runtime::database::Error::new(
|
||||
dever_runtime::database::Kind::NotFound,
|
||||
"account was not found",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Case {
|
||||
Count,
|
||||
Read,
|
||||
Update,
|
||||
}
|
||||
|
||||
fn measure(
|
||||
runtime: &Runtime,
|
||||
registry: &Arc<Registry>,
|
||||
generated: bool,
|
||||
case: Case,
|
||||
count: usize,
|
||||
) -> (u128, i64) {
|
||||
let mut context = Context::new(registry.clone());
|
||||
runtime
|
||||
.run(async |scope| {
|
||||
let mut checksum = 0i64;
|
||||
let start = Instant::now();
|
||||
for _ in 0..count {
|
||||
let value = if case != Case::Count {
|
||||
let owner = (case == Case::Update).then(|| context.enter());
|
||||
let row = if generated {
|
||||
let value = if case == Case::Update {
|
||||
GENERATED_UPDATE(
|
||||
dever_runtime::database::Id(1),
|
||||
"benchmark 中文",
|
||||
scope,
|
||||
&mut context,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
} else {
|
||||
GENERATED_READ(dever_runtime::database::Id(1), scope, &mut context)
|
||||
.await
|
||||
.unwrap()
|
||||
};
|
||||
RustAccount {
|
||||
id: value.field0,
|
||||
created_at: value.field1,
|
||||
name: value.field2,
|
||||
note: value.field3,
|
||||
uid: value.field4,
|
||||
rank: value.field5,
|
||||
}
|
||||
} else {
|
||||
rust_row(&mut context, scope, case == Case::Update)
|
||||
.await
|
||||
.unwrap()
|
||||
};
|
||||
if let Some(owner) = owner {
|
||||
context.finish(scope, owner, true).await.unwrap();
|
||||
}
|
||||
// 两侧都把完整记录交给调用者,不在 worker 内提前丢字段。
|
||||
let row = black_box(&row);
|
||||
assert_eq!(row.name, "benchmark 中文");
|
||||
assert!(row.note.is_none());
|
||||
row.id.0 + row.name.len() as i64
|
||||
} else if generated {
|
||||
GENERATED_COUNT(scope, &mut context).await.unwrap()
|
||||
} else {
|
||||
rust_count(&mut context, scope).await
|
||||
};
|
||||
checksum += black_box(value);
|
||||
}
|
||||
(start.elapsed().as_nanos(), checksum)
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let unique = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let relative = format!("database-benchmark-{}-{unique}", std::process::id());
|
||||
let directory = Directory(
|
||||
std::env::current_exe()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(&relative),
|
||||
);
|
||||
std::fs::create_dir(&directory.0).unwrap();
|
||||
let settings = directory.0.join("setting.json");
|
||||
let runtime = Runtime::new(Limits {
|
||||
threads: 1,
|
||||
workers: 1,
|
||||
tasks: 16,
|
||||
})
|
||||
.unwrap();
|
||||
let schema = database_settings(&runtime, &directory.0, &settings, &relative);
|
||||
let registry = runtime
|
||||
.run(async |scope| database_registry(&settings, scope).await)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
runtime
|
||||
.run(async |scope| {
|
||||
let mut context = Context::new(registry.clone());
|
||||
let created = GENERATED_CREATE("benchmark 中文", scope, &mut context)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(created.field0.0, 1);
|
||||
assert_eq!(
|
||||
GENERATED_COUNT(scope, &mut context).await.unwrap(),
|
||||
rust_count(&mut context, scope).await
|
||||
);
|
||||
let expected = (
|
||||
created.field0,
|
||||
created.field1,
|
||||
created.field2,
|
||||
created.field3,
|
||||
created.field4,
|
||||
created.field5,
|
||||
);
|
||||
let read = GENERATED_READ(ModelId(1), scope, &mut context)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
(
|
||||
read.field0,
|
||||
read.field1,
|
||||
read.field2,
|
||||
read.field3,
|
||||
read.field4,
|
||||
read.field5
|
||||
),
|
||||
expected
|
||||
);
|
||||
let read = rust_row(&mut context, scope, false).await.unwrap();
|
||||
assert_eq!(
|
||||
(
|
||||
read.id,
|
||||
read.created_at,
|
||||
read.name,
|
||||
read.note,
|
||||
read.uid,
|
||||
read.rank
|
||||
),
|
||||
expected
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
println!("case,sample,implementation,nanoseconds,checksum");
|
||||
for (name, case) in [
|
||||
("database_count", Case::Count),
|
||||
("database_get", Case::Read),
|
||||
("database_update_commit", Case::Update),
|
||||
] {
|
||||
if let Ok(selected) = std::env::var("DEVER_BENCH_CASES")
|
||||
&& !selected.split(',').any(|item| item == name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for generated in [true, false] {
|
||||
black_box(measure(&runtime, ®istry, generated, case, 200));
|
||||
}
|
||||
for sample in 0..15 {
|
||||
let mut order = [("dever", true), ("rust", false), ("control", true)];
|
||||
order.rotate_left(sample % 3);
|
||||
let mut expected = None;
|
||||
for (implementation, generated) in order {
|
||||
let (elapsed, checksum) = measure(&runtime, ®istry, generated, case, 2000);
|
||||
if let Some(expected) = expected {
|
||||
assert_eq!(checksum, expected);
|
||||
}
|
||||
expected = Some(checksum);
|
||||
println!("{name},{sample},{implementation},{elapsed},{checksum}");
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(registry);
|
||||
if let Some(schema) = schema {
|
||||
schema.close(&runtime);
|
||||
}
|
||||
drop(runtime);
|
||||
drop(directory);
|
||||
}
|
||||
Reference in new issue
Block a user