我正在研究一个简单的解析器,在进行分析时我发现瓶颈在...文件读取!我摘录了非常简单的测试来比较的性能fstreams和FILE*读取数据的大斑点时:
#include <stdio.h>
#include <chrono>
#include <fstream>
#include <iostream>
#include <functional>
void measure(const std::string& test, std::function<void()> function)
{
auto start_time = std::chrono::high_resolution_clock::now();
function();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start_time);
std::cout<<test<<" "<<static_cast<double>(duration.count()) * 0.000001<<" ms"<<std::endl;
}
#define BUFFER_SIZE (1024 * 1024 * 1024)
int main(int argc, const char * argv[])
{
auto buffer = new char[BUFFER_SIZE];
memset(buffer, 123, BUFFER_SIZE);
measure("FILE* write", [buffer]()
{
FILE* file = fopen("test_file_write", "wb");
fwrite(buffer, 1, BUFFER_SIZE, file);
fclose(file);
});
measure("FILE* read", [buffer]()
{
FILE* file = fopen("test_file_read", "rb");
fread(buffer, 1, BUFFER_SIZE, file);
fclose(file);
});
measure("fstream write", [buffer]()
{
std::ofstream stream("test_stream_write", std::ios::binary);
stream.write(buffer, BUFFER_SIZE);
});
measure("fstream read", [buffer]()
{
std::ifstream stream("test_stream_read", std::ios::binary);
stream.read(buffer, BUFFER_SIZE);
});
delete[] buffer;
}
Run Code Online (Sandbox Code Playgroud)
在我的机器上运行此代码的结果是:
FILE* write 1388.59 ms
FILE* read 1292.51 ms
fstream write 3105.38 ms
fstream read 3319.82 ms
Run Code Online (Sandbox Code Playgroud)
fstream写/读比FILE*写/读慢大约2倍!这是在阅读大量数据的同时,没有任何解析或其他功能fstreams.我在Mac OS,Intel I7 2.6GHz,16GB 1600 MHz Ram,SSD驱动器上运行代码.请注意,再次运行相同代码的时间FILE* read非常短(大约200毫秒)可能是因为文件被缓存...这就是为什么打开用于读取的文件不是使用代码创建的.
为什么在读取一大块二进制数据时使用fstream的速度是如此之慢FILE*?
编辑1:我更新了代码和时间.抱歉耽搁了!
编辑2:我添加了命令行和新结果(非常类似于以前的!)
$ clang++ main.cpp -std=c++11 -stdlib=libc++ -O3
$ ./a.out
FILE* write 1417.9 ms
FILE* read 1292.59 ms
fstream write 3214.02 ms
fstream read 3052.56 ms
Run Code Online (Sandbox Code Playgroud)
在第二次运行的结果之后:
$ ./a.out
FILE* write 1428.98 ms
FILE* read 196.902 ms
fstream write 3343.69 ms
fstream read 2285.93 ms
Run Code Online (Sandbox Code Playgroud)
看起来这个文件在读取时都会被缓存,FILE*并且stream随着时间的减少,两者的数量相同.
编辑3:我将代码缩减为:
FILE* file = fopen("test_file_write", "wb");
fwrite(buffer, 1, BUFFER_SIZE, file);
fclose(file);
std::ofstream stream("test_stream_write", std::ios::binary);
stream.write(buffer, BUFFER_SIZE);
Run Code Online (Sandbox Code Playgroud)
并启动了探查器.似乎stream在xsputn功能上花了很多时间,实际的write调用具有相同的持续时间(应该是,它是相同的函数......)
Running Time Self Symbol Name
3266.0ms 66.9% 0,0 std::__1::basic_ostream<char, std::__1::char_traits<char> >::write(char const*, long)
3265.0ms 66.9% 2145,0 std::__1::basic_streambuf<char, std::__1::char_traits<char> >::xsputn(char const*, long)
1120.0ms 22.9% 7,0 std::__1::basic_filebuf<char, std::__1::char_traits<char> >::overflow(int)
1112.0ms 22.7% 2,0 fwrite
1127.0ms 23.0% 0,0 fwrite
Run Code Online (Sandbox Code Playgroud)
编辑4由于某种原因,此问题被标记为重复.我想指出我根本不使用printf,我只std::cout用来写时间.read部件中使用的文件是部件的输出write,使用不同的名称复制以避免缓存
Mat*_*son 18
看来,在Linux上,对于这一大量数据,实现fwrite效率更高,因为它使用write而不是writev.
我不确定为什么writev这么慢write,但这似乎是差异所在.fstream在这种情况下,我认为为什么需要使用该构造,我完全没有理由.
通过使用strace ./a.out(a.out程序在哪里测试)可以很容易地看出这一点.
输出:
fstream的:
clock_gettime(CLOCK_REALTIME, {1411978373, 114560081}) = 0
open("test", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
writev(3, [{NULL, 0}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1073741824}], 2) = 1073741824
close(3) = 0
clock_gettime(CLOCK_REALTIME, {1411978386, 376353883}) = 0
write(1, "fstream write 13261.8 ms\n", 25fstream write 13261.8 ms) = 25
Run Code Online (Sandbox Code Playgroud)
文件*:
clock_gettime(CLOCK_REALTIME, {1411978386, 930326134}) = 0
open("test", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1073741824) = 1073741824
clock_gettime(CLOCK_REALTIME, {1411978388, 584197782}) = 0
write(1, "FILE* write 1653.87 ms\n", 23FILE* write 1653.87 ms) = 23
Run Code Online (Sandbox Code Playgroud)
我没有他们花哨的SSD驱动器,所以我的机器会慢一点 - 或者在我的情况下其他东西更慢.
正如Jan Hudec指出的那样,我误解了结果.我刚刚写了这个:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <chrono>
void measure(const std::string& test, std::function<void()> function)
{
auto start_time = std::chrono::high_resolution_clock::now();
function();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start_time);
std::cout<<test<<" "<<static_cast<double>(duration.count()) * 0.000001<<" ms"<<std::endl;
}
#define BUFFER_SIZE (1024 * 1024 * 1024)
int main()
{
auto buffer = new char[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
measure("writev", [buffer]()
{
int fd = open("test", O_CREAT|O_WRONLY);
struct iovec vec[] =
{
{ NULL, 0 },
{ (void *)buffer, BUFFER_SIZE }
};
writev(fd, vec, sizeof(vec)/sizeof(vec[0]));
close(fd);
});
measure("write", [buffer]()
{
int fd = open("test", O_CREAT|O_WRONLY);
write(fd, buffer, BUFFER_SIZE);
close(fd);
});
}
Run Code Online (Sandbox Code Playgroud)
这是实际的fstream实现,做了一些愚蠢的事情 - 可能是以小块,某处,某种方式或类似的东西复制整个数据.我会试着进一步了解.
结果对于这两种情况几乎完全相同,并且比问题中的两者fstream和FILE*变体都快.
编辑:
现在看来,在我的机器上,如果你fclose(file)在写入之后添加它,它们需要大约相同的时间,fstream并且FILE*- 在我的系统上,大约13秒写入1GB - 使用旧式旋转磁盘类型驱动器,不是SSD.
但是,我可以使用此代码更快地写入:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <chrono>
void measure(const std::string& test, std::function<void()> function)
{
auto start_time = std::chrono::high_resolution_clock::now();
function();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start_time);
std::cout<<test<<" "<<static_cast<double>(duration.count()) * 0.000001<<" ms"<<std::endl;
}
#define BUFFER_SIZE (1024 * 1024 * 1024)
int main()
{
auto buffer = new char[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
measure("writev", [buffer]()
{
int fd = open("test", O_CREAT|O_WRONLY, 0660);
struct iovec vec[] =
{
{ NULL, 0 },
{ (void *)buffer, BUFFER_SIZE }
};
writev(fd, vec, sizeof(vec)/sizeof(vec[0]));
close(fd);
});
measure("write", [buffer]()
{
int fd = open("test", O_CREAT|O_WRONLY, 0660);
write(fd, buffer, BUFFER_SIZE);
close(fd);
});
}
Run Code Online (Sandbox Code Playgroud)
给出大约650-900毫秒的时间.
我也可以编辑原始程序,给大约1000毫秒的时间fwrite- 只需删除fclose.
我还添加了这个方法:
measure("fstream write (new)", [buffer]()
{
std::ofstream* stream = new std::ofstream("test", std::ios::binary);
stream->write(buffer, BUFFER_SIZE);
// Intentionally no delete.
});
Run Code Online (Sandbox Code Playgroud)
然后这里也需要大约1000毫秒.
所以,我的结论是,不知何故,有时,关闭文件会使其刷新到磁盘.在其他情况下,它没有.我还是不明白为什么......
TL;DR:在编写之前尝试将其添加到您的代码中:
const size_t bufsize = 256*1024;
char buf[bufsize];
mystream.rdbuf()->pubsetbuf(buf, bufsize);
Run Code Online (Sandbox Code Playgroud)
使用 处理大文件时fstream,请确保使用流缓冲区。
与直觉相反,禁用流缓冲会显着降低性能。至少 MSVC 实现一次将1 个字符复制到filebuf未设置缓冲区时(请参阅 参考资料streambuf::xsputn()),这会使您的应用程序受 CPU 限制,从而导致 I/O 速率降低。
注意:您可以在此处找到完整的示例应用程序。
| 归档时间: |
|
| 查看次数: |
10656 次 |
| 最近记录: |