这波操作看麻了!十亿行数据,从71s到1.7s的优化之路。
这波操作看麻了!十亿行数据,从71s到1.7s的优化之路。
在2024年1月份,一场名为《The One Billion Row Challenge》(简称1BRC)的编程挑战赛吸引了众多Java开发者的关注。这场挑战要求参赛者解析一个包含10亿行气象数据的文件,并计算出每个气象站的最小、最大和平均温度。本文将详细解析这场挑战的赛题要求、优化过程以及最终的优化成果。
赛题介绍
在2024年1月1日,Gunnar Morling发布了一篇关于1BRC挑战的文章。赛题要求解析一个包含10亿行气象数据的文件,每行记录了一个气象站的温度值,格式为"气象站;温度",温度值保留一位小数。参赛者需要计算出每个气象站的最小、最大和平均温度,并按照字典序输出结果。
为了帮助理解,这里给出一个简短的例子:
chengdu;12.0
guangzhou;7.2
chengdu;6.3
beijing;-3.6
chengdu;23.0
shanghai;9.8
chengdu;24.3
beijing;17.8
对于成都(chengdu),最低气温是6.3,最高气温是24.3,平均气温是(12.0+6.3+23.0+24.3)/4=16.4。
出题人还提供了一个基线版本的代码实现,使用了流式编程,但在作者的电脑上运行时间接近14分钟。
优化过程
巨人肩膀:Marko Topolnik的优化之路
在官方GitHub项目中,Marko Topolnik分享了他的优化过程,从最初的71秒优化到了1.7秒。以下是他的优化步骤:
最常规的代码
首先,他给出了一个常规实现的代码,使用并行流处理数据。在Hetzner CCX33机器上,运行时间为71秒。
第0版优化:更换JVM
作者尝试使用GraalVM替代默认的JVM,运行时间从71秒减少到66秒。
数据指标的重要性
在正式优化之前,作者使用了三个重要的工具来收集程序的运行指标,包括火焰图和GC情况。
第一版优化:并行I/O
通过分析火焰图,发现性能瓶颈主要在BufferedReader和字符串处理上。作者采用MemorySegment进行并行I/O读取,将运行时间从66秒减少到17秒。
第二版优化:优化温度解析
将温度解析为整数,避免了字符串到浮点数的转换,运行时间进一步减少到11秒。
第三版优化:自定义哈希表
由于只有413个气象站,作者设计了一个自定义的哈希表,使用开放寻址法解决冲突,将运行时间降低到6.6秒。
第四版优化:使用Unsafe和SWAR
这一版使用了Unsafe避免边界检查,使用SWAR技术处理数据,将运行时间降低到2.4秒。
第五版优化:统计学应用
通过统计分析发现,气象站名称长度的分布特点,进一步优化字符串比较逻辑,将运行时间降低到1.8秒。
进一步优化
作者还尝试了消除启动/清理成本和使用更小的文件分块等优化手段,最终将运行时间优化到1.7秒。
总结
这场挑战赛展示了Java编程中各种高级优化技术的应用,包括并行I/O、自定义数据结构、位操作优化等。虽然这些优化在实际业务开发中可能并不常见,但它们展示了Java编程的深度和可能性。对于Java开发者来说,了解这些优化技巧,不仅可以提升代码性能,更能拓宽技术视野。