Feat: improve bench query code in rust example, add usage at Readme

This commit is contained in:
gongzhengyang 2025-09-24 14:35:42 +08:00
parent b6410e16d0
commit d3592eb7cd
5 changed files with 101 additions and 65 deletions

View File

@ -66,7 +66,7 @@ fn main() {
}
```
## Benchmark 测试
## Cache policy benchmark
```bash
$ git lfs pull
@ -111,3 +111,53 @@ Found 6 outliers among 100 measurements (6.00%)
4 (4.00%) high severe
// --snip--
```
## 测试与结果验证benchmark
```bash
$ git lfs pull
$ cd binding/rust/example
$ cargo build -r
```
构建的执行程序位置 `binding/rust/target/release/searcher`
#### 测试 IPv6
```bash
$ cd binding/rust
$ ./target/release/searcher --xdb='../../data/ip2region_v6.xdb' query
ip2region xdb searcher test program, type `quit` or `Ctrl + c` to exit
ip2region>> 2001:5:4::
region: Ok("荷兰|北荷兰省|阿姆斯特丹|专线用户"), took: 284.80775ms
ip2region>> 2001::
region: Ok("美国|加利福尼亚州|洛杉矶|专线用户"), took: 12.75µs
ip2region>> 2001:5:6::
region: Ok("荷兰|北荷兰省|阿姆斯特丹|专线用户"), took: 52.958µs
ip2region>> 2001:5:5::
region: Ok("比利时|弗拉芒大区|泽勒|专线用户"), took: 123.375µs
ip2region>>
```
#### 测试 IPv4
```bash
$ cd binding/rust
$ ./target/release/searcher --xdb='../../data/ip2region_v4.xdb' query
ip2region xdb searcher test program, type `quit` or `Ctrl + c` to exit
ip2region>> 1.1.2.1
region: Ok("中国|福建省|福州市|电信"), took: 5.342625ms
ip2region>> 2.2.21.1
region: Ok("法国|0|0|橘子电信"), took: 25.667µs
ip2region>>
```
#### Benchmark 与验证结果
通过 searcher 程序来测试性能,同时依据 ip sources 文件对比查询结果,检测是否存在错误
```bash
$ cd binding/rust/example
$ cargo build -r
## 通过 data/ip2region_v4.xdb 和 data/ipv4_source.txt 进行 ipv4 的 bench 测试:
$ RUST_LOG=debug ../target/release/searcher --xdb='../../../data/ip2region_v4.xdb' bench '../../../data/ipv4_source.txt'
## 通过 data/ip2region_v6.xdb 和 data/ipv6_source.txt 进行 ipv6 的 bench 测试:
$ RUST_LOG=debug ../target/release/searcher --xdb='../../../data/ip2region_v6.xdb' bench '../../../data/ipv6_source.txt'
```

View File

@ -1,6 +1,6 @@
[package]
name = "rust-example"
default-run = "rust-example"
name = "searcher"
default-run = "searcher"
version = "0.2.0"
edition = "2024"
rust-version = "1.89.0"
@ -13,3 +13,4 @@ license = "Apache-2.0"
ip2region = { path = "../ip2region" }
clap = { version = "4.5", features = ["derive", "env"] }
tracing-subscriber = "0.3"
tracing = "0.1"

View File

@ -12,9 +12,9 @@ use clap::{Parser, Subcommand, ValueEnum};
///
/// cd binding/rust/example
///
/// cargo run -r -- --xdb=$XDB bench $CHECK
/// ./searcher --xdb=$XDB bench $CHECK
///
/// cargo run -r -- --xdb=$XDB query
/// ./searcher --xdb=$XDB query
///
/// ```
#[derive(Parser)]

View File

@ -1,5 +1,3 @@
extern crate core;
use std::fs::File;
use std::io::Write;
use std::io::{BufRead, BufReader};
@ -7,49 +5,52 @@ use std::net::IpAddr;
use std::str::FromStr;
use std::time::Instant;
use crate::cmd::{Action, CmdCachePolicy, Command};
use clap::Parser;
use tracing::info;
use ip2region::{CachePolicy, Searcher};
use crate::cmd::{Action, CmdCachePolicy, Command};
mod cmd;
macro_rules! perform_check {
($searcher:expr, $start_ip:expr, $end_ip:expr, $check:expr) => {{
let start_ip = $start_ip;
let end_ip = $end_ip;
let mid_ip = (start_ip >> 1) + (end_ip >> 1);
let checks = [
start_ip,
(start_ip >> 1) + (mid_ip >> 1),
mid_ip,
(mid_ip >> 1) + (end_ip >> 1),
end_ip,
];
for ip in checks.iter() {
if *ip > start_ip || *ip < end_ip {
// IP not in start - end ip
// This happens when start ip equals end ip
continue;
}
let result = $searcher.search(*ip).unwrap();
assert_eq!(result.as_str(), $check);
}
checks.len()
}};
}
fn check(searcher: &Searcher, start_ip: IpAddr, end_ip: IpAddr, check: &str) -> usize {
match (start_ip, end_ip) {
(IpAddr::V4(start_ip), IpAddr::V4(end_ip)) => {
let start_ip = u32::from(start_ip);
let end_ip = u32::from(end_ip);
let mid_ip = (start_ip >> 1) + (end_ip >> 1);
let checks = [
start_ip,
(start_ip >> 1) + (mid_ip >> 1),
mid_ip,
(mid_ip >> 1) + (end_ip >> 1),
end_ip,
];
for ip in checks.iter() {
let result = searcher.search(*ip).unwrap();
assert_eq!(result.as_str(), check);
(IpAddr::V4(original_start_ip), IpAddr::V4(original_end_ip)) => {
let start_ip = u32::from(original_start_ip);
let end_ip = u32::from(original_end_ip);
perform_check!(searcher, start_ip, end_ip, check)
}
checks.len()
}
(IpAddr::V6(start_ip), IpAddr::V6(end_ip)) => {
let start_ip = u128::from(start_ip);
let end_ip = u128::from(end_ip);
let mid_ip = (start_ip >> 1) + (end_ip >> 1);
let checks = [
start_ip,
(start_ip >> 1) + (mid_ip >> 1),
mid_ip,
(mid_ip >> 1) + (end_ip >> 1),
end_ip,
];
for ip in checks.iter() {
let result = searcher.search(*ip).unwrap();
assert_eq!(result.as_str(), check);
}
checks.len()
(IpAddr::V6(original_start_ip), IpAddr::V6(original_end_ip)) => {
let start_ip = u128::from(original_start_ip);
let end_ip = u128::from(original_end_ip);
perform_check!(searcher, start_ip, end_ip, check)
}
_ => panic!("invalid start ip and end ip"),
}
@ -59,35 +60,18 @@ fn bench(searcher: &Searcher, check_filepath: &str) {
let file = File::open(check_filepath).unwrap();
let reader = BufReader::new(file);
let lines = reader.lines().take(100_000).collect::<Vec<_>>();
let now = Instant::now();
let mut count = 0;
for line in lines {
let line = line.unwrap();
if !line.contains('|') {
continue;
}
for line in reader.lines().map_while(Result::ok) {
let ip_test_line = line.splitn(3, '|').collect::<Vec<&str>>();
if ip_test_line.len() != 3 {
panic!("this line {line} don`t have enough `|` for spilt");
}
if ip_test_line.len() == 3 {
let start_ip = IpAddr::from_str(ip_test_line[0]).unwrap();
let end_ip = IpAddr::from_str(ip_test_line[1]).unwrap();
if end_ip < start_ip {
panic!("start ip({start_ip}) should not be greater than end ip({end_ip})")
}
{
count += check(searcher, start_ip, end_ip, ip_test_line[2]);
}
}
println!(
"Bench finished, total: {count},\
took: {:?} ,\
cost: {:?}/op",
now.elapsed(),
now.elapsed() / count as u32
)
info!(count, took=?now.elapsed(), avg_took=?(now.elapsed() / (count as u32)), "Benchmark finished");
}
fn query(searcher: &Searcher) {

View File

@ -3,4 +3,5 @@ mod header;
mod ip_value;
mod searcher;
pub use self::searcher::{CachePolicy, Searcher};
pub use searcher::{CachePolicy, Searcher};
pub use ip_value::IpValueExt;