简体   繁体   English

Haskell或F#高吞吐量二进制I / O.

[英]Haskell or F# high throughput binary I/O

How good is the performance of binary I/O libraries in these two languages> I am contemplating about re-writing an ugly (yet very fast) C++ code that processes binary files of around 5-10GB using standard fread and fwrite functions. 这两种语言中二进制I / O库的性能有多好>我正在考虑重新编写一个丑陋(但非常快)的C ++代码,它使用标准的fread和fwrite函数处理大约5-10GB的二进制文件。 What slow-down factor should I expect for an optimized implementation in F# and Haskell? 对于F#和Haskell中的优化实现,我应该期待什么减速因子?

EDIT: here is the C implementation of counting zero-bytes (buffer allocated on heap). 编辑:这是计算零字节的C实现(堆上分配的缓冲区)。

#include <stdio.h>
#include <stdlib.h>

#define SIZE 32*1024
int main(int argc, char* argv[])
{
    FILE *fp;
    char *buf;
    long i = 0, s = 0, l = 0;
    fp = fopen(argv[1], "rb");
    if (!fp) {
        printf("Openning %s failed\n", argv[1]);
        return -1;
    }
    buf = (char *) malloc(SIZE);
    while (!feof(fp)) {
        l = fread(buf, 1, SIZE, fp);
        for (i = 0; i &lt l; ++i) {
            if (buf[i] == 0) {
                ++s;
            }
        }
    }
    printf("%d\n", s);
    fclose(fp);
    free(buf);
    return 0;
}

The results: 结果:


$ gcc -O3 -o ioc io.c
$ ghc --make -O3 -o iohs io.hs
Linking iohs ...
$ time ./ioc 2.bin
462741044

real    0m16.171s
user    0m11.755s
sys     0m4.413s
$ time ./iohs 2.bin
4757708340

real    0m16.879s
user    0m14.093s
sys     0m2.783s
$ ls -lh 2.bin
-rw-r--r-- 1  14G Jan  4 10:05 2.bin

Haskell using lazy ByteString-based IO, with a "binary" parser should be around the same performance as C code doing the same job, on the same data types. Haskell使用基于ByteString的惰性 IO,使用“二进制”解析器应该与执行相同作业的C代码在相同的数据类型上具有相同的性能。

The key packages to be aware of: 要注意的关键包:

Considering that this post entails: 考虑到这篇文章需要:

  • Haskell 哈斯克尔
  • code optimizations 代码优化
  • performance benchmarks 绩效基准

...it's safe to say that I'm in way over my head. ......可以肯定地说我已经超越了我的头脑。 Nevertheless, I always learn something when I get in over my head, so here goes. 然而,当我进入我的脑海时,我总是学到一些东西,所以这里就是这样。

I went spelunking around the Data.ByteString.Lazy.* Haskell modules via Hoogle and found the length function for measuring the length of a lazy ByteString. 我通过Hoogle围绕Data.ByteString.Lazy.* Haskell模块进行了探索,并找到了用于测量惰性ByteString长度的长度函数。 It is implemented thus: 因此实施:

length :: ByteString -> Int64
length cs = foldlChunks (\n c -> n + fromIntegral (S.length c)) 0 cs

Hmm. 嗯。 Jon did say that "...Folding over chunks of file in the F# is a major part of why it is fast..." (my emphasis). Jon确实说过“......在F#中折叠文件是其快速的主要原因......”(我的重点)。 And this length function appears to be implemented using a chunky fold as well. 并且这个length函数似乎也使用粗大的折叠来实现。 So it appears that this function is much more of an 'apples to apples' comparison to Jon's F# code. 因此,看起来这个函数更像是与Jon的F#代码进行“苹果对苹果”的比较。

Does it make a difference in practice? 它在实践中有所作为吗? I compared Jon's example to the following: 我将Jon的例子与以下内容进行了比较:

import System
import Data.List
import Data.ByteString.Lazy as B

main =
    getArgs
    >>= B.readFile . Data.List.head
    >>= print . B.length

Jon's Haskell example on my machine for a 1.2 GB file: 10.5s Jon的Haskell示例在我的机器上为1.2 GB文件: 10.5s

The 'chunky' version: 1.1s '矮胖'版本: 1.1s

The 'chunky' version of the Haskell code is nearly ten times faster. Haskell代码的“矮胖”版本快了近十倍 Which suggests that it is probably multiple times faster than Jon's optimized F# code. 这表明它可能比Jon的优化F#代码快几倍。

EDIT 编辑

While I don't necessarily completely agree with Jon's criticisms of my example, I would like to make it as impeachable as possible. 虽然我不一定完全赞同Jon对我的例子的批评,但我想尽可能地使它成为可能的。 As such, I have profiled the following code: 因此,我已经描述了以下代码:

import System
import Data.List
import Data.ByteString.Lazy as B

main =
    getArgs
    >>= B.readFile . Data.List.head
    >>= print . B.count 0

This code loads the contents of the target file into a ByteString and then 'counts' each occurence of a 0-value byte. 此代码将目标文件的内容加载到ByteString中,然后“计数”每个0值字节的出现。 Unless I'm missing something, this program must load and evaluate each byte of the target file. 除非我遗漏了某些东西,否则该程序必须加载并评估目标文件的每个字节。

The above program runs consistently about 4x faster than the latest fastest Haskell program submitted by Jon, copied here for reference (in case it is updated): 上述程序的运行速度始终比Jon提交的最快的Haskell程序快4倍,复制在此作为参考(如果已更新):

import System
import Data.Int
import Data.List
import Data.ByteString.Lazy as B

main =
    getArgs
    >>= B.readFile . Data.List.head
    >>= print . B.foldl (\n c -> n + 1) (0 :: Data.Int.Int64)

我在这里写博客。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM