gpt4 book ai didi

linux - 在读取带有惰性字节串的大文件时减少内核开销

转载 作者:行者123 更新时间:2023-12-04 21:01:16 24 4
gpt4 key购买 nike

我正在读取一个大文件(1-10 GB)并计算一些简单的统计数据,比如计算一个字符。在这种情况下流式传输是有意义的,所以我使用惰性 ByteString s。特别是我的main好像

import qualified Data.ByteString.Lazy as BSL

main :: IO ()
main = do
contents <- BSL.readFile "path"
print $ computeStats contents

详情 computeStats在这种情况下可能并不重要。

当使用 +RTS -sstderr 运行它时,我看到了这个:
MUT     time    0.938s  (  1.303s elapsed)

注意 CPU 时间和运行时间之间的差异。除此之外,在 /usr/bin/time 下运行显示类似的结果:
0.89user 0.45system 0:01.35elapsed

我正在测试的文件位于 tmpfs ,所以实际的磁盘性能不应该是一个因素。

如何减少 system在这种情况下时间?我尝试明确设置文件句柄的缓冲区大小(对运行时间没有统计上的显着影响)以及 mmap读取文件并将其包装成 ByteString (运行时间实际上变得更糟)。还有什么值得尝试的?

最佳答案

首先,您的机器似乎出了点问题。当我在缓存在内存或 tmpfs 文件系统中的 1G 文件上运行此程序时(不管哪个),系统时间要小得多:

1.44user 0.14system 0:01.60elapsed 99%CPU (0avgtext+0avgdata 50256maxresident)

如果您有任何其他类型的负载或内存压力可能导致这些额外的 300 毫秒,我认为您需要先解决这个问题,然后我在下面说的任何内容都会有所帮助,但是......

无论如何,对于我的测试,我使用了更大的 5G 测试文件来使系统时间更容易量化。作为基线,C 程序:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUFLEN (1024*1024)
char buffer[BUFLEN];

int
main()
{
int nulls = 0;
int fd = open("/dev/shm/testfile5G.dat", O_RDONLY);
while (read(fd, buffer, BUFLEN) > 0) {
for (int i = 0; i < BUFLEN; ++i) {
if (!buffer[i]) ++nulls;
}
}
printf("%d\n", nulls);
}

gcc -O2 编译多次在我的测试文件上运行:
real    0m2.035s
user 0m1.619s
sys 0m0.416s

作为比较,使用 ghc -O2 编译的 Haskell 程序:
import Data.Word
import qualified Data.ByteString.Lazy as BSL

main :: IO ()
main = do
contents <- BSL.readFile "/scratch/buhr/testfile5G.dat"
print $ BSL.foldl' go 0 contents
where go :: Int -> Word8 -> Int
go n 0 = n + 1
go n _ = n

计数要慢一些,但系统时间几乎相同:
real    0m8.411s
user 0m7.966s
sys 0m0.444s

更简单的测试,例如 cat testfile5G.dat >/dev/null都给出了一致的系统时间结果,因此可以安全地得出 read 的开销的结论。调用,很可能是将数据从内核复制到用户地址空间的特定过程,占大约 410 毫秒系统时间的相当一部分。

与您上面的经验相反,切换到 mmap应该减少这种开销。 Haskell 程序:
import System.Posix.IO
import Foreign.Ptr
import Foreign.ForeignPtr
import MMAP
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BS

-- exact length of file
len :: Integral a => a
len = 5368709120

main :: IO ()
main = do
fd <- openFd "/scratch/buhr/testfile5G.dat" ReadOnly Nothing defaultFileFlags
ptr <- newForeignPtr_ =<< castPtr <$>
mmap nullPtr len protRead (mkMmapFlags mapPrivate mempty) fd 0
let contents = BS.fromForeignPtr ptr 0 len
print $ BS.foldl' (+) 0 contents

以大约相同的用户时间运行,但大大减少了系统时间:
real    0m7.972s
user 0m7.791s
sys 0m0.181s

请注意,使用零拷贝方法将映射区域变为严格的 ByteString 是绝对关键的。这里。

在这一点上,我认为我们可能归结为管理进程页表的开销,以及使用 mmap 的 C 版本:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>

size_t len = 5368709120;

int
main()
{
int nulls = 0;
int fd = open("/scratch/buhr/testfile5G.dat", O_RDONLY);
char *p = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
for (int i = 0; i < len; ++i) {
if (!p[i]) ++nulls;
}
printf("%d\n", nulls);
}

具有相似的系统时间:
real    0m1.888s
user 0m1.708s
sys 0m0.180s

关于linux - 在读取带有惰性字节串的大文件时减少内核开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60008229/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com