1brc - 十亿行挑战 —— 使用 Java 对文本文件中的 10 亿行数据进行聚合的有趣探索

Created at: 2023-12-28 17:13:24
Language: Java
License: Apache-2.0

1️🐝🏎️ ⃣ 十亿行挑战

状态 1 月 1 日:此挑战现已开放提交!

十亿行挑战赛 (1BRC) 是一个有趣的探索,探讨了现代 Java 在聚合文本文件中的 10 亿行时可以走多远。抓住你所有的(虚拟)线程,联系SIMD,优化你的GC,或采取任何其他技巧,并创建最快的实现来解决这个任务!

1BRC公司

文本文件包含一系列气象站的温度值。每行是 格式为 的一个测量值,测量值正好有一个小数位数。下面以 10 行为例:

<string: station name>;<double: measurement>

Hamburg;12.0
Bulawayo;8.9
Palembang;38.8
St. John's;15.2
Cracow;12.6
Bridgetown;26.9
Istanbul;6.2
Roseau;34.4
Conakry;31.2
Istanbul;23.0

任务是编写一个 Java 程序,该程序读取文件,计算每个气象站的最小、平均值和最高温度值,并在 stdout 上发出结果,如下所示(即按站点名称的字母顺序排序,每个站点的结果值以 格式四舍五入为一个小数位数):

<min>/<mean>/<max>

{Abha=-23.0/18.0/59.2, Abidjan=-16.2/26.0/67.3, Abéché=-10.0/29.4/69.0, Accra=-10.1/26.4/66.4, Addis Ababa=-23.7/16.0/67.0, Adelaide=-27.8/17.3/58.5, ...}

在 2024 年 1 月 31 日之前提交你的实施,并成为排行榜的一部分!

结果

# 结果 (m:s.ms) 实现 JDK的 提交者
1. 00:11.495 链接 21.0.1-格拉尔斯 艾略特·巴拉斯
2. 00:11:886 链接 21.0.1-开放 迪米塔尔·季米特洛夫
3. 00:14.848 链接 21.0.1-格拉尔斯 山姆·普拉拉
4. 00:18.865 链接 21.0.1-开放 尼克·帕尔默
5. 00:21.853 链接 21.0.1-格拉尔 菲利普·赫里萨福夫
6. 00:23.366 链接 21.0.1-开放 罗伊·范·莱恩
7. 00:38.106 链接 21.0.1-开放 马库斯·埃布纳
8. 00:38.510 链接 21.0.1-开放 汉普斯拉姆
9. 00:38.819 链接 21.0.1-开放 理查德·斯塔丁
10. 00:50.547 链接 21.0.1-开放 奥勒良·图图亚努
11. 00:53.679 链接 21.0.1-开放 克里斯·里科米尼
12. 00:57.141 链接 21.0.1-开放 罗曼·史怀哲
13. 02:00.101 链接 21.0.1-开放 khmarbaise(赫马尔拜斯酒店)
14. 02:08.315 链接 21.0.1-开放 伊塔斯克
15. 02:08.650 链接 21.0.1-开放 库杜瓦·克沙夫拉姆
16. 04:13.449 链接(基线) 21.0.1-开放 贡纳尔·莫林

请参阅下文,了解如何通过自己的实施进入挑战赛。

先决条件

必须在系统上安装 Java 21

挑战赛

此存储库包含两个程序:

  • dev.morling.onebrc.CreateMeasurements
    (通过 create_measurements.sh 调用):在此项目的根目录中创建文件测量.txt,其中包含可配置数量的随机测量值
  • dev.morling.onebrc.CalculateAverage
    (通过calculate_average.sh调用):计算文件测量值的平均值.txt

执行以下步骤以运行质询:

  1. 使用 Apache Maven 构建项目:

    ./mvnw clean verify
    
  2. 创建包含 1B 行的测量文件(仅一次):

    ./create_measurements.sh 1000000000
    

    这将需要几分钟时间。注意:生成的文件大小约为 12 GB,因此请确保有足够的磁盘空间。

  3. 计算平均测量值:

    ./calculate_average.sh
    

    提供的朴素示例实现使用 Java 流 API 处理文件,并在用于结果评估的环境中在 ~2 分钟内完成任务。它可作为比较你自己的实现的基线。

  4. 优化它:

    调整程序以加快速度,以你认为合适的任何方式(只需遵守下面描述的一些规则)。选项包括并行化计算、使用(孵化)Vector API、同时内存映射文件的不同部分、使用 AppCDS、GraalVM、CRaC 等来加速应用程序启动、选择和调整垃圾回收器等等。

    CalculateAverage

火焰图/剖面图

一个提示是,如果你安装了 jbang,你可以通过运行以下命令来获取程序的火焰图:

jbang --javaagent=ap-loader@jvm-profiling-tools/ap-loader=start,event=cpu,file=profile.html -m dev.morling.onebrc.CalculateAverage_yourname target/average-1.0.0-SNAPSHOT.jar

或直接在 .java 文件上:

jbang --javaagent=ap-loader@jvm-profiling-tools/ap-loader=start,event=cpu,file=profile.html src/main/java/dev/morling/onebrc/CalculateAverage_yourname

当你运行此命令时,它将在 profile.html 中生成火焰图。然后,你可以在浏览器中打开它,并查看你的程序将时间花在哪里。

规则和限制

  • 可以使用以下任何 Java 发行版:
    • SDKMan 提供的任何构建
    • 可以使用 openjdk.net 上可用的抢先体验版本(包括 OpenJDK 项目的 EA 版本,如 Valhalla)
    • 基于 builds.shipilev.net 构建 如果你想使用无法通过这些渠道获得的构建,请联系讨论是否可以考虑。
  • 不得使用外部库依赖项
  • 实现必须作为单个源文件提供
  • 计算必须在应用程序运行时进行,即你不能在构建时处理测量文件(例如,使用 GraalVM 时),而只是将结果烘焙到二进制文件中

参加挑战赛

要将你自己的实施提交给 1BRC,请按照以下步骤操作:

  • 创建 onebrc GitHub 存储库的分支。
  • 创建 CalculateAverage.java 的副本,命名为 CalculateAverage_<your_GH_user>.java,例如 CalculateAverage_doloreswilson.java
  • 加快实施速度。真的很快。
  • 创建calculate_average.sh的副本,命名为 calculate_average_<your_GH_user>.sh,例如 calculate_average_doloreswilson.sh
  • 调整该脚本,使其引用实现类名称。如果需要,通过该脚本中的变量提供任何 JVM 参数。
    JAVA_OPTS
  • OpenJDK 21 是默认值。如果需要自定义 JDK 构建,请在应用程序启动之前在启动 shell 脚本中包含 SDKMAN 命令。
    sdk use java [version]
  • (可选)如果要使用本机二进制文件 (GraalVM),请调整 pom.xml 文件,以便它生成该二进制文件。
  • 针对上游存储库创建拉取请求,明确说明
    • 实现类的名称。
    • 程序在系统上的执行时间和规格(CPU、内核数、RAM)。这仅供参考,官方运行时间将如下所述确定。
  • 我将运行该程序并确定其性能,如下一节所述,并将结果输入记分牌。

注意:如果我对实施有疑问,我保留不评估具体提交的权利(即我不会;)运行你的比特币矿工。

如果你想与社区讨论实施 1BRC 的任何潜在想法,你可以使用此存储库的 GitHub Discussions。请保持友好和文明。

挑战赛将持续到2024年1月31日。在 UTC 时间 2024 年 1 月 31 日 23:59 之后创建的任何提交(即拉取请求)将不予考虑。

评估结果

结果通过在 Hetzner Cloud CCX33 实例(8 个专用 vCPU、32 GB RAM)上运行程序来确定。该程序用于测量执行时间,即测量端到端时间。每个竞争者将连续运行五次。最慢和最快的运行将被丢弃。其余三次运行的平均值是该竞争者的结果,并将添加到上面的结果表中。完全相同的测量 .txt 文件用于评估所有竞争者。

time

如果你想在 Hetzner Cloud 上启动自己的测试盒,你可能会发现这些设置脚本(基于 Terraform 和 Ansible)很有用。请注意,这将产生你负责的费用,我不会支付你的云账单:)

如果你参加此挑战,你可能会学到新东西,激励他人,并为看到你的名字列在上面的记分牌中而感到自豪。有传言说,获胜者还可能获得一件独特的⃣ 1️🐝🏎️ T恤!

常见问题

问:我可以使用 Kotlin 或 Java 以外的其他 JVM 语言吗?
答:不,这个挑战只针对 Java。不过,请随意非正式地分享明显优于任何列出的结果的实现。

问:我可以使用非 JVM 语言和/或工具吗?
答:不,这个挑战只针对 Java。不过,请随时非正式地分享有趣的实现和结果。例如,看看 DuckDB 在这项任务中的表现会很有趣。

问:我有一个实现,但它不是用 Java 实现的。我可以在某个地方分享吗?
答:虽然非 Java 解决方案不能正式提交到挑战赛中,但欢迎你在 Show and Tell GitHub 讨论区分享它们。

问:我可以使用 JNI 吗?
答:提交必须完全用 Java 实现,即你不能用 C/C++ 编写 JNI 胶水代码。不过,你可以通过 GraalVM 使用 Java 代码的 AOT 编译,方法是对整个应用程序进行 AOT 编译,或者通过创建本机库(请参阅此处)。

问:测量 .txt 文件的编码是什么?
答:该文件使用 UTF-8 编码。

问:我可以对数据集中显示的气象站名称进行假设吗?
答:不可以,虽然数据集生成器只使用一组固定的站点名称,但任何解决方案都应使用任意 UTF-8 站点名称(为简单起见,名称保证不包含任何字符)。

;

问:是否可以从其他提交中复制代码?答:可以。挑战的主要重点是学习新东西,而不是“获胜”。当你这样做时,请注明相关来源提交的内容。请不要重新提交其他没有或只有微不足道的改进的参赛作品。

Q: 为什么 1️⃣🐝🏎️
答:是项目名称的缩写:One Billion Row Challenge。

许可证

此代码库在 Apache 许可证版本 2 下可用。

行为准则

对彼此优秀!除了获胜之外,这项挑战的目的是玩得开心并学习新东西。