计算机通常依赖于二进制浮点数。大多数情况下,它们跨越64位或32位。许多编程语言将它们称为double和float。 JavaScript默认情况下使用64位二进制浮点数字类型表示其所有数字。
人类通常以十进制表示数字,例如0.1或1e-1。因此,许多系统使用ASCII文本以十进制表示形式存储数字。该软件必须从二进制浮点数转换为ASCII并返回。关于序列化(从二进制浮点数到ASCII)的工作很多,但是反序列化(从ASCII到二进制浮点数)的工作却相对较少。
通常,读取十进制数并将其转换为二进制浮点数很慢。多慢通常约为200 MB / s。如果您有快速磁盘,它的速度将比磁盘慢得多。 PlayStation 5的磁盘带宽超过5 GB / s。
您可以做得更好。最后,我发表了一篇手稿,解释了一种更好的方法:以每秒千兆字节的速度解析数字。不要错过本文的“致谢”部分:这是与真正聪明的人的共同努力。
本文中的基准测试主要基于C ++库fast_float。该库需要符合C ++ 11标准的编译器。它提供了一些函数,它们紧密模拟浮点和双精度类型的标准C ++ from_chars函数。 Apache Arrow和Yandex ClickHouse使用它。它也是世界上最快的Yaml库的一部分。这些from_char函数是C ++ 17标准的一部分。据我所知,目前只有Microsoft实现了该功能:它们在GNU GCC中不可用。
在使用实际数据文件(加拿大)的Apple M1 MacBook上,我们得到的fast_float可以远远超过每秒1 GB,并且接近2 GB / s。默认的Apple标准库提供的常规C函数(strtod)在此基准上的表现非常差。
感谢Nigel Tao和其他出色的工程师,该方法的简化版本现在已成为Go标准库的一部分。它加速了Go处理,同时有助于提供准确的解析。奈杰尔·陶(Nigel Tao)有一篇不错的帖子,标题为《 Eisel-Lemire ParseNumberF64算法》。
那Rust呢? 有一个Rust端口。 毫不奇怪,Rust版本在速度上与C ++版本匹配。 以下是使用相同文件和相同处理器(Apple M1)的结果: C#端口正在开发中,初步结果表明我们可以以合理的优势击败标准库。 我希望今年能有一个Swift和Java移植(邀请帮助和倡议)。 视频。 去年,我在Go Systems Conf SF 2020上做了一个演讲,题目是具有精确度的浮点数解析(GB / sec)。 它在YouTube上。