作者:温绍锦(高铁) 阿里云计算平台团队
在Parser场景,包括SQL Parser和JSON Parser,如何更快检测空白字符是一个提升性能的关键点。笔者有多年SQL Parser和JSON Parser的经验,把我所知道的一些检测空白的方法分享给大家。
如果采用json.org的标准,空白字符包括:
'\b' -- ASCII 8
'\t' -- ASCII 9
'\n' -- ASCII 10
'\f' -- ASCII 12
'\r' -- ASCII 13
' ' -- ASCII 32
JDK的Character提供了isWhiteSpace方法,逻辑不能定制化,不支持上面的'\b'字符判空,但为了性能比较,也加入进来。
boolean space = Character.isWhitespace(ch);
这个常规办法,用6个并列的or判断。所有的字符检测都需要做6次判断,性能较差。
boolean space = ch == ' '
|| ch == '\n'
|| ch == '\r'
|| ch == '\f'
|| ch == '\t'
|| ch == '\b';
由于空白字符最大的是空格ASCII 32,在方法2的基础上先做一个预先检测,这样就能获得非常好的性能,但当输入的有大量空白字符\b时,就就会面临方法2的问题。fastjson 1.x ( GitHub - alibaba/fastjson: A fast JSON parser/generator for Java. )判空用的是这个方法。
boolean space = ch <= ' '
&& (ch == ' '
|| ch == '\n'
|| ch == '\r'
|| ch == '\f'
|| ch == '\t'
|| ch == '\b');
这种算法,在JDK 17下性能有较大提升。在大量输入是非空字符时,性能并不出色。
boolean space;
switch (ch) {
case ' ':
case '\n':
case '\r':
case '\t':
case '\b':
case '\f':
space = true;
break;
default:
space = false;
break;
}
通过预计算一个常量的long,然后做bitAnd判断。
fastjson2 ( GitHub - alibaba/fastjson2: FASTJSON2是FASTJSON项目的重要升级,目标是为下一个十年提供一个高性能的JSON库 )判空用的是这个方法。
static final long SPACE = (1L << ' ')
| (1L << '\n')
| (1L << '\r')
| (1L << '\f')
| (1L << '\t')
| (1L << '\b');
boolean space = ch <= ' ' && ((1L << ch) & SPACE) != 0;
使用阿里云x64当前代计算型4核8G服务器,CPU型号 Intel Xeon(Ice Lake) Platinum 8369B
使用阿里云ARM当前代计算型4核8G服务器,CPU型号 Ampere Altra / AltraMax
下载Oracle最新的LTS版本Linux JDK
jdk1.8.0_333_x64
jdk-11.0.15.1_x64
jdk-17.0.3.1_x64
jdk1.8.0_333_aarch64
jdk-11.0.15.1_aarch64
jdk-17.0.3.1_aarch64
{"images": [{
"height":768,
"size":"LARGE",
"title":"Javaone Keynote",
"uri":"http://javaone.com/keynote_large.jpg",
"width":1024
}, {
"height":240,
"size":"SMALL",
"title":"Javaone Keynote",
"uri":"http://javaone.com/keynote_small.jpg",
"width":320
}
],
"media": {
"bitrate":262144,
"duration":18000000,
"format":"video/mpg4",
"height":480,
"persons": [
"Bill Gates",
"Steve Jobs"
],
"player":"JAVA",
"size":58982400,
"title":"Javaone Keynote",
"uri":"http://javaone.com/keynote.mpg",
"width":640
}
}
测试代码:
fastjson2/benchmark/src/main/java/com/alibaba/fastjson2/benchmark at main · alibaba/fastjson2 · GitHub
执行测试代码
git clone https://github.com/alibaba/fastjson2
cd fastjson2
mvn clean install -Dmaven.test.skip
java -cp benchmark/target/fastjson2-benchmarks.jar com.alibaba.fastjson2.benchmark.SpaceCheckBenchmark
JDK8 | JDK11 | JDK17 | |
---|---|---|---|
方法1 | 1756.884 | 1692.215 | 1770.658 |
方法2 | 1911.820 | 1866.888 | 1903.105 |
方法3 | 3496.629 | 4228.972 | 3956.434 |
方法4 | 2798.679 | 2910.525 | 2876.148 |
方法5 | 3522.462 | 3694.007 | 4474.286 |
### jdk1.8.0_333_x64
Benchmark Mode Cnt Score Error Units
SpaceCheckBenchmark.CharacterIsWhitespace thrpt 5 1756.884 ? 2.202 ops/ms
SpaceCheckBenchmark.spaceOr thrpt 5 1911.820 ? 5.965 ops/ms
SpaceCheckBenchmark.spaceOrPreCheck thrpt 5 3496.629 ? 739.373 ops/ms
SpaceCheckBenchmark.spaceSwitch thrpt 5 2798.679 ? 1024.227 ops/ms
SpaceCheckBenchmark.spaceBitAnd thrpt 5 3522.462 ? 859.084 ops/ms
### jdk-11.0.15.1_x64
Benchmark Mode Cnt Score Error Units
SpaceCheckBenchmark.CharacterIsWhitespace thrpt 5 1692.215 ? 363.925 ops/ms
SpaceCheckBenchmark.spaceOr thrpt 5 1866.888 ? 33.836 ops/ms
SpaceCheckBenchmark.spaceOrPreCheck thrpt 5 4228.972 ? 212.870 ops/ms
SpaceCheckBenchmark.spaceSwitch thrpt 5 2910.525 ? 584.406 ops/ms
SpaceCheckBenchmark.spaceBitAnd thrpt 5 3694.007 ? 158.193 ops/ms
### jdk-17.0.3.1_x64
Benchmark Mode Cnt Score Error Units
SpaceCheckBenchmark.CharacterIsWhitespace thrpt 5 1770.658 ? 651.168 ops/ms
SpaceCheckBenchmark.spaceOr thrpt 5 1903.105 ? 40.520 ops/ms
SpaceCheckBenchmark.spaceOrPreCheck thrpt 5 3956.434 ? 628.745 ops/ms
SpaceCheckBenchmark.spaceSwitch thrpt 5 2876.148 ? 127.401 ops/ms
SpaceCheckBenchmark.spaceBitAnd thrpt 5 4474.286 ? 539.261 ops/ms
JDK8 | JDK11 | JDK17 | |
---|---|---|---|
方法1 | 911.795 | 785.339 | 1269.834 |
方法2 | 789.439 | 833.830 | 842.177 |
方法3 | 2304.419 | 2429.907 | 2146.953 |
方法4 | 880.387 | 1124.967 | 1540.419 |
方法5 | 2363.957 | 2392.123 | 2570.536 |
### jdk1.8.0_333_aarch64
Benchmark Mode Cnt Score Error Units
SpaceCheckBenchmark.CharacterIsWhitespace thrpt 5 911.795 ? 5.171 ops/ms
SpaceCheckBenchmark.spaceOr thrpt 5 789.439 ? 163.867 ops/ms
SpaceCheckBenchmark.spaceOrPreCheck thrpt 5 2304.419 ? 29.643 ops/ms
SpaceCheckBenchmark.spaceSwitch thrpt 5 880.387 ? 63.411 ops/ms
SpaceCheckBenchmark.spaceBitAnd thrpt 5 2363.957 ? 759.335 ops/ms
### jdk-11.0.15.1_aarch64
Benchmark Mode Cnt Score Error Units
SpaceCheckBenchmark.CharacterIsWhitespace thrpt 5 785.339 ? 5.361 ops/ms
SpaceCheckBenchmark.spaceOr thrpt 5 833.830 ? 14.402 ops/ms
SpaceCheckBenchmark.spaceOrPreCheck thrpt 5 2429.907 ? 9.120 ops/ms
SpaceCheckBenchmark.spaceSwitch thrpt 5 1124.967 ? 811.435 ops/ms
SpaceCheckBenchmark.spaceBitAnd thrpt 5 2392.123 ? 381.551 ops/ms
### jdk-17.0.3.1_aarch64
Benchmark Mode Cnt Score Error Units
SpaceCheckBenchmark.CharacterIsWhitespace thrpt 5 1269.834 ? 5.587 ops/ms
SpaceCheckBenchmark.spaceOr thrpt 5 842.177 ? 7.969 ops/ms
SpaceCheckBenchmark.spaceOrPreCheck thrpt 5 2146.953 ? 518.320 ops/ms
SpaceCheckBenchmark.spaceSwitch thrpt 5 1540.419 ? 32.401 ops/ms
SpaceCheckBenchmark.spaceBitAnd thrpt 5 2570.536 ? 2.284 ops/ms
综合来看,无论是x64还是aarch64,方法5性能最理想。