我正在编写一个函数来读取二进制文件,这些文件被组织为一系列(键,值)对,其中键是小的ASCII字符串,值是int或双重存储的二进制格式。
如果天真地实现,这个函数会大量调用fread来读取非常少量的数据(通常不超过10个字节)。尽管fread在内部使用缓冲区来读取文件,但我已经实现了自己的缓冲区,并且我在Linux和Windows上都观察到速度提高了10倍。 fread使用的缓冲区大小足够大,函数调用不能对这种减速负责。所以我去挖掘了fread的GNU实现并发现了对文件的一些锁定,以及许多其他的事情,比如验证文件是否已打开,具有读访问权等等。难怪为什么畏惧如此缓慢。
但是,如果多个线程可以在相同的文件上调用fread,那么fread是线程安全的理由是什么呢?这对我来说是令人难以置信的。这些要求让它变得如此慢。有什么好处?
答案 0 :(得分:0)
想象一下,你有一个文件,其中每个5字节可以并行处理(比方说,图像中的像素):
123456789A
一个线程需要选择5个字节“12345”,下一个5个字节需要“6789A”。
如果它不是线程安全的,则不同的线程可能会拾取错误的块。例如:“12367”和“4589A”甚至最差(意外行为,重复字节或最差)。
答案 1 :(得分:0)
正如nemequ
所建议的那样:
请注意,如果您使用的是glibc,则可以使用_unlocked变体(*例如,fread_unlocked)。在Windows上,您可以定义_CRT_DISABLE_PERFCRIT_LOCKS
答案 2 :(得分:0)
流I / O已经像糖蜜一样慢。程序员认为从主存储器读取(比CPU周期长1000倍)是年龄。从物理磁盘或网络读取也可能是永恒的。
我不知道这是否是图书馆实施者可以添加锁定开销的第一个原因,但我保证它起了重要作用。
是的,它减慢了速度,但正如您所发现的那样,您可以手动缓冲读取并使用您自己的处理来提高性能真正重要的速度。 (这是关键 - 当你绝对必须尽快读取数据时。在一般情况下不要打扰手动缓冲。)
那是 合理化。我相信你能想到更多!