最近在写 Flutter 应用,发现 Dart 解析比较大的 json 比较慢,会影响 UI 线程,使用 compute 的话,的确不影响 UI 线程了,但是解析起来更慢,想着可以使用 flutter rust bridge
做个 json 解析库,不过我用 rust 写个测试程序,发现解析 json 比 dart 快不了多少,各位有啥好意见没?
各语言测试:
time node index.js 1.37s user 0.19s system 121% cpu 1.282 total
time go build && ./main 1.59s user 0.02s system 80% cpu 2.002 total
time python3.9 main.py 2.45s user 0.27s system 98% cpu 2.762 total
time cargo run --release 2.46s user 0.42s system 97% cpu 2.972 total
time dart compile exe main.dart && bin/main 4.62s user 0.45s system 114% cpu 4.415 total
rust code:
use std::fs::File;
use std::io::{Read};
use serde_json::Value;
fn parse_json(contents: Vec<u8>) {
let now = std::time::Instant::now();
let _: Value = serde_json::from_slice(&contents).unwrap();
let elapsed = now.elapsed();
println!("elapsed: {:?}", elapsed);
}
fn main() {
let contents = {
let mut vec = Vec::new();
// https://github.com/json-iterator/test-data/blob/master/large-file.json
File::open("large-file.json").unwrap().read_to_end(&mut vec).unwrap();
vec
};
for _ in 0..10 {
parse_json(contents.clone());
}
}
测试机器:MacBook Pro (16-inch, 2019) 2.3 GHz 八核 Intel Core i9 32 GB 2667 MHz DDR4
不好意思,上面使用time统计时间是不包含程序编译时间的,只是发主题的时候合到一起的。
for 循环打印的结果如下:
NodeJs: 110 ms
NodeJs: 107 ms
NodeJs: 103 ms
NodeJs: 106 ms
NodeJs: 128 ms
NodeJs: 94 ms
NodeJs: 93 ms
NodeJs: 92 ms
NodeJs: 98 ms
NodeJs: 93 ms
Dart: 384 ms
Dart: 390 ms
Dart: 377 ms
Dart: 395 ms
Dart: 356 ms
Dart: 381 ms
Dart: 378 ms
Dart: 370 ms
Dart: 388 ms
耗时: 379 ms
Python: 241.057 ms
Python: 233.375 ms
Python: 249.352 ms
Python: 244.968 ms
Python: 249.487 ms
Python: 288.286 ms
Python: 263.347 ms
Python: 255.939 ms
Python: 256.224 ms
Python: 252.851 ms
Golang: 171 ms
Golang: 162 ms
Golang: 160 ms
Golang: 161 ms
Golang: 161 ms
Golang: 160 ms
Golang: 165 ms
Golang: 157 ms
Golang: 161 ms
Golang: 158 ms
Rust: 297.331601ms
Rust: 268.03852ms
Rust: 280.606788ms
Rust: 272.826854ms
Rust: 293.53427ms
Rust: 269.841906ms
Rust: 285.450976ms
Rust: 288.862249ms
Rust: 275.984671ms
Rust: 276.715469ms
1
MoYi123 2023-07-05 10:28:46 +08:00
为什么要把编译的时间也算进去?
|
2
codehz 2023-07-05 10:32:43 +08:00 via iPhone 1
你可以考虑一下解析 json 的目的,如果可行的话,不需要先变成一个对象,而是直接从流式解析中提取需要的数据
|
4
newmlp 2023-07-05 10:40:36 +08:00
你这都把程序启动时间算上了,肯定不准啊,json 解析才能用多少时间估计 1ms 都不到
|
5
tool2d 2023-07-05 11:02:21 +08:00
纯好奇,用自己写的库解析了一下主贴里的 24M json ,竟然要 1 秒。
发现 dart 这些库,已经很强了。 |
6
wxf666 2023-07-05 11:03:48 +08:00
你要存啥呢?用个普通数据库也不错呀。。
比如,SQLite 解析 JSON 也挺快: ``` sqlite> .timer on sqlite> SELECT json_array_length(readFile('large-file.json')); 11351 Run Time: real 0.089 user 0.046875 sys 0.046875 ``` |
7
visper 2023-07-05 11:05:57 +08:00
rust 比 python 还慢.谁说 rust 快的
|
8
lisongeee 2023-07-05 11:07:15 +08:00
json 解析函数可以改成异步嘛?拆成多个子任务
let btachTaskNum = 0; while(isParsing){ dobtachTask(); btachTaskNum++; if(btachTaskNum%1000==0){ await nextTick() } } |
9
tool2d 2023-07-05 11:07:36 +08:00
二楼说的也对,看具体使用目的,如果预处理一次,全部变成二进制数转节点,变成特定格式,载入和查询起来都是很快的。
你要暴力遍历巨量文本的 json ,速度上不去。要动态查询某些节点,速度还是可以提升一下的。 |
10
lisongeee 2023-07-05 11:09:19 +08:00
艹,我刚刚我回答不符合题意,当作没看见吧
|
11
tool2d 2023-07-05 11:17:24 +08:00
@lisongeee 其实可以优化成异步或者多线程版本,那个 large-file.json 结构和 csv 一样,一行行很有规律。
但是不是所有的 json 都能这样有规律的拆分,层次结构过于复杂,也没办法拆分。 |
12
iOCZ 2023-07-05 11:18:43 +08:00
1 秒都不到怎么能算慢呢
|
13
iOCZ 2023-07-05 11:19:05 +08:00
我们的人生是需要转圈圈的
|
14
debuggerx 2023-07-05 11:21:49 +08:00 via Android
数据量大的时候就应该考虑是不是不应该用 json
|
15
lisongeee 2023-07-05 11:47:04 +08:00
|
16
tool2d 2023-07-05 11:51:20 +08:00
|
17
duke807 2023-07-05 11:52:29 +08:00 via Android
不如弃用 json 改用和 json 类似但支持二进制的 msgpack
|
19
icyalala 2023-07-05 12:25:34 +08:00
你要用 C/C++ 的那些库比如 simdjson ,帖子里的 24M json 也就十几 ms
|
20
x77 2023-07-05 12:45:13 +08:00
- 异步解析
- 缓存(避免重复的解析) - 减少 Json 的体积 - 改进设计,不用 Json 存储巨量数据 |
21
flyqie 2023-07-05 12:53:26 +08:00 via Android
json 真心不适合储存大量的数据。。
大量数据用 binary 比较好办。。 |
22
lysS 2023-07-05 13:44:02 +08:00
数据够大,调 ffi 有提升
|
23
honhon 2023-07-05 14:22:51 +08:00
数据量大 json 不适合
|
24
mxT52CRuqR6o5 2023-07-05 14:26:26 +08:00 via Android
如果结论没问题的话,应该就是 dartvm 比较菜了
|
25
janus77 2023-07-05 14:27:06 +08:00
如果是一次性,那么就一次解析完了重复使用
如果不是一次性,该考虑换 protobuf ,大数据用 json 本来就是不合理的选择,或者改需求了,在移动端设备上是否需要执行这种复杂逻辑 |
26
mxT52CRuqR6o5 2023-07-05 14:29:33 +08:00
还有一个问题,你说 dart 慢会「影响 UI 线程」,说明你在开发安卓
但 nodejs 等其他语言的测试应该都是直接跑在 mac 上的 这个性能是不太好直接比较的 |
27
Kaiv2 2023-07-05 15:28:43 +08:00
Mac Air M1 2020
Rust: elapsed: 149.987542ms elapsed: 107.580541ms elapsed: 105.902334ms elapsed: 105.940375ms elapsed: 105.132084ms elapsed: 105.841583ms elapsed: 105.093ms elapsed: 105.630708ms elapsed: 105.344959ms elapsed: 105.7735ms Java: elapsed: 263ms elapsed: 189ms elapsed: 44ms elapsed: 50ms elapsed: 41ms elapsed: 56ms elapsed: 39ms elapsed: 53ms elapsed: 39ms elapsed: 39ms |
30
serco 2023-07-05 15:51:53 +08:00
@simman serde_json 浪费了不少时间在建 struct 上,不知道具体数据类型的话,还不如 json 这种 crate 快。
json parse 这种常规操作各个语言都有优化,差距不会特别大的,而且还要看具体做到了哪一步,是真的 parse 完建立了对应的类型,还是处在类似 pre-parsing 的状态临查询了才真的实例对应的类型。 |
31
Kaiv2 2023-07-05 15:52:21 +08:00
@ljsh093
Java: Fastjson 1.2.83 elapsed: 349ms elapsed: 107ms elapsed: 88ms elapsed: 98ms elapsed: 79ms elapsed: 77ms elapsed: 78ms elapsed: 85ms elapsed: 111ms elapsed: 61ms Java: Fastjson2 2.0.34 elapsed: 214ms elapsed: 65ms elapsed: 71ms elapsed: 62ms elapsed: 53ms elapsed: 46ms elapsed: 46ms elapsed: 46ms elapsed: 46ms elapsed: 49ms |
33
smirkcat 2023-07-05 16:41:45 +08:00
用字节的 ast 解析引擎
|
34
icyalala 2023-07-05 16:45:43 +08:00
@Kaiv2 再来个 C++ 的,也是 M1
simdjson: 18.44 ms 16.54 ms 16.05 ms 16.77 ms 16.41 ms yyjson: 14.66 ms 14.37 ms 14.00 ms 13.89 ms 13.68 ms rapidjson: 68.54 ms 67.82 ms 67.06 ms 67.04 ms 66.88 ms |
35
GiantHard 2023-07-05 17:15:46 +08:00
@serco #30
说得对,crate json 要比 serde_json 快一倍( AMD Ryzen 7 5800U ) ```rs // build with cargo build -r use std::fs::File; use std::io::{Read}; use serde_json::Value; use json; fn parse_json(contents: &Vec<u8>) { let now = std::time::Instant::now(); let _: Value = serde_json::from_slice(&contents).unwrap(); let elapsed = now.elapsed(); println!("serde_json: {:?}", elapsed); } fn parse_json2(contents: &str) { let now = std::time::Instant::now(); let _ = json::parse(&contents).unwrap(); let elapsed = now.elapsed(); println!("json: {:?}", elapsed); } fn main() { let contents = { let mut vec = Vec::new(); // https://github.com/json-iterator/test-data/blob/master/large-file.json File::open("large-file.json").unwrap().read_to_end(&mut vec).unwrap(); vec }; for _ in 0..10 { parse_json(&contents); } for _ in 0..10 { // create json str from contents let contents = String::from_utf8(contents.clone()).unwrap(); parse_json2(&contents); } } ``` ``` serde_json: 182.566705ms serde_json: 157.330185ms serde_json: 151.551179ms serde_json: 150.997928ms serde_json: 158.290287ms serde_json: 151.983899ms serde_json: 152.493476ms serde_json: 150.337436ms serde_json: 151.174303ms serde_json: 150.424319ms json: 71.080736ms json: 73.125015ms json: 80.531158ms json: 82.744729ms json: 79.73645ms json: 80.040878ms json: 80.30521ms json: 79.455428ms json: 79.702968ms json: 72.22127ms ________________________________________________________ Executed in 2.42 secs fish external usr time 2.36 secs 0.00 micros 2.36 secs sys time 0.06 secs 464.00 micros 0.06 secs ``` 当然,你换个编程语言也可以比 serde_json 更快 ```fs /// build with dotnet build -c Release open System.IO open System.Text.Json open System.Diagnostics let json = File.ReadAllText("large-file.json") for i in 1..10 do let sw = Stopwatch.StartNew() let jsonDocument = JsonDocument.Parse(json) sw.Stop() // print in ms printfn "Elapsed: %dms" sw.ElapsedMilliseconds printf "Done" ``` ``` Elapsed: 185ms Elapsed: 74ms Elapsed: 73ms Elapsed: 70ms Elapsed: 75ms Elapsed: 74ms Elapsed: 67ms Elapsed: 69ms Elapsed: 73ms Elapsed: 73ms Done ________________________________________________________ Executed in 963.57 millis fish external usr time 881.76 millis 0.00 micros 881.76 millis sys time 86.87 millis 387.00 micros 86.48 millis ``` |
36
nuk 2023-07-05 17:32:12 +08:00
可以分块解析,或者自己做个匹配算法,理论上 json 解析只要不分配内存其实是很快的。那如果又要全部解析,又要快,那只能调用别的了,但是几百毫秒的这么做意义不大。
|
37
zibber 2023-07-06 00:07:04 +08:00
用 c++写个 api, 然后直接掉 c++的接口
|
38
kingzeus 2023-07-06 09:51:28 +08:00
1. 用 C++
2. dart 的话,可以考虑异步 Future/Isolate 3. 预处理,改成更加高效的数据格式 |