bench script

This commit is contained in:
lion 2025-10-14 14:20:19 +08:00
parent 4d71b47e05
commit d35a7b9120
4 changed files with 150 additions and 6 deletions

View File

@ -38,8 +38,9 @@
"homepage": "https://github.com/lionsoul2014/ip2region#readme",
"devDependencies": {
"@types/jest": "^30.0.0",
"argparse": "^2.0.1",
"jest": "^30.2.0",
"ts-jest": "^29.4.5",
"typescript": "^5.9.3"
}
}
}

View File

@ -15,6 +15,7 @@ import {
export class Searcher {
constructor(version, dbPath, vectorIndex, cBuffer) {
this.ioCount = 0;
this.dbPath = dbPath;
this.version = version;
if (cBuffer != null) {
this.handle = null;
@ -112,6 +113,13 @@ export class Searcher {
fs.close(this.handle);
}
}
toString() {
const vn = this.version.name;
const vi = this.vectorIndex == null ? 'null' : this.vectorIndex.length;
const cf = this.cBuffer == null ? 'null' : this.cBuffer.length;
return `{"version": ${vn}, "dbPath": ${this.dbPath}, "handle": ${this.handle}, "vectorIndex": ${vi} "cBuffer": ${cf}}`;
}
}
export function newWithFileOnly(version, dbPath) {

View File

@ -0,0 +1,137 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
// app to do the xdb bench
// @Author Lion <chenxin619315@gmail.com>
import * as xdb from '../index.js';
import {ArgumentParser} from 'argparse';
import readline from 'node:readline';
import fs from 'fs';
const parser = new ArgumentParser({
add_help: true,
description: 'ip2region bench script',
prog: 'node tests/bench.app.js',
usage: 'Usage %(prog)s [command options]'
});
parser.add_argument('--db', {help: 'ip2region binary xdb file path'});
parser.add_argument('--src', {help: 'source ip text file path'});
parser.add_argument('--cache-policy', {help: 'cache policy: file/vectorIndex/content, default: vectorIndex'});
const args = parser.parse_args();
const dbPath = args.db || '';
const srcPath = args.src || '';
const cachePolicy = args.cache_policy || 'vectorIndex';
// create the searcher
const createSearcher = () => {
const handle = fs.openSync(dbPath, 'r');
// verify the xdb file
// @Note: do NOT call it every time you create a searcher since this will slow
// down the search response.
// @see the verify function for details.
xdb.verify(handle);
// get the ip version from the header
const version = xdb.versionFromHeader(xdb.loadHeader(handle));
let searcher = null;
switch(cachePolicy) {
case 'file':
searcher = xdb.newWithFileOnly(version, dbPath);
break;
case 'vectorIndex':
const vIndex = xdb.loadVectorIndexFromFile(dbPath);
searcher = xdb.newWithVectorIndex(version, dbPath, vIndex);
break;
case 'content':
const cBuffer = xdb.loadContentFromFile(dbPath);
searcher = xdb.newWithBuffer(version, cBuffer);
break;
default:
fs.closeSync(handle);
throw new Error(`invalid cache-policy '${cachePolicy}'`);
}
fs.closeSync(handle);
return searcher;
}
const _split = (line) => {
const ps = [];
const s1 = line.indexOf('|');
if (s1 === -1) {
ps.push(line);
return ps;
}
ps.push(line.substring(0, s1));
const s2 = line.indexOf('|', s1 + 1);
if (s2 === -1) {
ps.push(line.substring(s1+1));
return ps;
}
ps.push(line.substring(s1 + 1, s2));
ps.push(line.substring(s2 + 1));
return ps;
}
const getMs = () => {
const hrTime = process.hrtime();
return hrTime[0] * 1000000 + hrTime[1] / 1000;
}
// console.log(`dbPath=${dbPath}, src=${src}, cachePolicy=${cachePolicy}`);
const main = () => {
if (dbPath.length < 1 || srcPath.length < 1) {
parser.print_help();
return;
}
const searcher = createSearcher();
console.log(`Searcher: ${searcher.toString()}`);
let totalNs = 0, count = 0;
// read the source line and do the search bench
const rl = readline.createInterface({
input: fs.createReadStream(srcPath),
crlfDelay: Infinity
});
rl.on('line', async (l) => {
const ps = _split(l);
const sTime = process.hrtime();
const sip = xdb.parseIP(ps[0]);
const eip = xdb.parseIP(ps[1]);
if (xdb.ipCompare(sip, eip) > 0) {
throw new Error(`start ip(${ps[0]}) should not be greater than end ip(${ps[1]})`);
}
const test_list = [sip, eip];
for (let i = 0; i < test_list.length; i++) {
const region = await searcher.search(test_list[i]);
if (region != ps[2]) {
throw new Error(`failed to search(${xdb.ipToString(test_list[i])}) with (${region} != ${ps[2]})`);
}
count++;
}
totalNs += process.hrtime(sTime);
}).on('error', (err) => {
console.log(err);
process.exit(1);
});
process.on('exit', (code) => {
const tookSec = totalNs / 1e9;
const _eachUs = count == 0 ? 0 : totalNs / 1e3 / count;
console.log(`Bench finished, {cachePolicy: ${cachePolicy}, total: ${count}, took: ${tookSec}s, cost: ${_eachUs} μs/op}`);
});
}
main();

View File

@ -303,19 +303,17 @@ export function versionFromName(name) {
}
export function versionFromHeader(h) {
let v = h.version();
// old structure with ONLY IPv4 supporting
if (v == XdbStructure20) {
if (h.version == XdbStructure20) {
return IPv4;
}
// structure 3.0 with IPv6 supporting
if (v != XdbStructure30) {
if (h.version != XdbStructure30) {
return null;
}
let ipVer = h.ipVersion();
let ipVer = h.ipVersion;
if (ipVer == XdbIPv4Id) {
return IPv4;
} else if (ipVer == XdbIPv6Id) {