diff --git a/binding/rust/ReadMe.md b/binding/rust/ReadMe.md index 8667b54..32c5a76 100644 --- a/binding/rust/ReadMe.md +++ b/binding/rust/ReadMe.md @@ -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' +``` \ No newline at end of file diff --git a/binding/rust/example/Cargo.toml b/binding/rust/example/Cargo.toml index e36c708..1fe8ebf 100644 --- a/binding/rust/example/Cargo.toml +++ b/binding/rust/example/Cargo.toml @@ -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" diff --git a/binding/rust/example/src/cmd.rs b/binding/rust/example/src/cmd.rs index a8cae94..67d450d 100644 --- a/binding/rust/example/src/cmd.rs +++ b/binding/rust/example/src/cmd.rs @@ -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)] diff --git a/binding/rust/example/src/main.rs b/binding/rust/example/src/main.rs index aefac8f..f5853b2 100644 --- a/binding/rust/example/src/main.rs +++ b/binding/rust/example/src/main.rs @@ -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); - } - checks.len() + (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) } - (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::>(); 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::>(); - if ip_test_line.len() != 3 { - panic!("this line {line} don`t have enough `|` for spilt"); - } - 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})") - } - { + 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(); 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) { diff --git a/binding/rust/ip2region/src/lib.rs b/binding/rust/ip2region/src/lib.rs index 5a70527..f892edc 100644 --- a/binding/rust/ip2region/src/lib.rs +++ b/binding/rust/ip2region/src/lib.rs @@ -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;