【问题标题】:Performance issue with big file reading/writing in C++C++ 中大文件读/写的性能问题
【发布时间】:2012-11-30 12:39:36
【问题描述】:

我正在通过 Qt 开发一个应用程序。

在这个应用程序中,主线程是一个 Web 服务器。另一个线程有时会从大文件 (250mb) 中读取数据并将它们写入输出文件 (~2gb)。

该线程对文件执行高 I/O 操作,CPU iowait 约为 70%。

我的问题是写入文件时,网络服务器没有快速响应。我的理解是服务器的 qt 套接字(在 Linux 上)由连接到 poll 或 select 事件系统的系统套接字表示。所以 Qt 只有在 poll 发出事件时才会向我的应用程序发送信号。

我认为文件写入的 io 操作太大可能会阻塞轮询系统,所以我的 qt 服务器没有收到套接字事件。当线程写完数据后,一切就正常了。

文件写法如下:

while(dataToRead){
    // context has the list of files to read and current step
    dataToRead = extractData(context, &pBuffer, &sizeBuf);

    fwrite (pBuffer, 1, sizeBuf, pOutFile);

    free(pBuffer);
    pBuffer = NULL;

    // usleep(100000);
}

如果我使用 usleep 功能添加中断,这有助于避免问题,但如果我没有使用足够大的睡眠,则不能完全避免。但是过大的sleep会破坏性能,而i是尽可能快的生成文件。

我做错了什么?尽可能快地读/写文件是否安全?在上述功能中睡眠是强制性的吗?但是我们怎么知道好的时间片呢?

我正在开发 Mint LMDE、Linux 3.2.0 64 位、Intel Core i5 2500 和标准 HDD 驱动器。

编辑: 可在此处获得重现该问题的示例程序:https://bugreports.qt-project.org/secure/attachment/30436/TestQtBlocked.zip。需要qt的qmake编译。如果你运行它,它将创建一个空的 3GB 文件,工作线程将在启动时启动,并在几秒钟内创建文件。在此期间,如果您尝试连接到 http://localhost:8081/ 并运行许多 F5 刷新页面,您会发现有时它没有快速响应。 如果有人可以用我的示例程序重现我的问题并告诉我,这可能会有所帮助。

【问题讨论】:

  • 你要写多大的数据块?即 sizeBuf 的正常值是多少?
  • @Mike:它是 Qt,所以它是 C++。为什么他会使用cstdio 而不是fstream 我无法理解。
  • @nos: 数据是JPEG图像,所以它可以从20Kb到200kb不等,sizeBuf不是静态的并且对应于图像大小。
  • @Mike:事实上,Qt 是针对我程序的高级部分,它使用用 C 或 c++ 代码编写的模块,但不使用 qt。
  • @Mike:250Mb 包含大量图片,extractData 函数在文件中搜索所需图片并将其复制到缓冲区中。是的,文件写入是在另一个线程中完成的。首先,我认为这是一个 Qt 问题,所以我制作了一个简单的测试程序来重现该问题。你可以在这里找到它:bugreports.qt-project.org/browse/QTBUG-28539

标签: c++ linux qt sockets io


【解决方案1】:

如果您正在饿死主线程的选择调用,请创建一个单独的线程来执行文件 I/O。当事件来自 Qt 时,触发某种 IPC,唤醒您的工作线程以执行大文件 I/O 并立即从您的事件处理程序返回。

(这假设异步写入文件对您的程序逻辑有意义。只有您才能确定这是否属实。)

【讨论】:

  • 这不是我真正想要的。正如我在 cmets 中所说,在我的程序中,我有一个 Web 服务器,允许我从 GUI 创建文件操作任务,辅助线程必须处理任务并执行 IO 操作。在处理任务时,它会阻止我的 Web 服务器的套接字事件,并且 GUI 不容易访问。所以实际上,IO 文件已经在一个线程中完成,但这与 Qt 无关。这只是 Web 服务器请求。
  • 文件线程是Qt使用的还是完全独立的线程?您如何从网络服务器线程向文件线程发出信号?
  • 是的,文件线程是主线程之外的另一个线程。当我创建一个任务时,它进入一个队列,并且工作线程收到一个新任务待处理的事件。它开始处理文件写入。我也尝试将网络服务器放在另一个线程中,但它并没有解决问题。如果您需要更多源代码,我在上面发布了一个示例程序,可以重现该问题。
【解决方案2】:

来自手册页: size_t fwrite(const void *ptr, size_t size, size_t nmemb, 文件*流);

你想写 sizeBuf ,1个元素。

您可能希望使用 setvbuf 调整缓冲。

setvbuf(pOutfile, NULL, _IONBF, 0) - 禁用缓冲。

完整示例位于: http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Frtref%2Fsetvbuf.htm

更好地切换到使用文件描述符,而不是文件流。

使用文件描述符,您可以使用 sendfile 和 slice。 人发送文件 人切片

【讨论】:

  • sendfile 现在只适用于套接字,但您可以通过使用 mmap 获得同样的好处
  • 我尝试反转大小和计数,并在有或没有 setvbuf 函数的情况下使用它,但它没有任何改变。
  • Eric Beuque - 测试程序中缓慢的部分是不断分配/释放内存,实际代码中是这种情况吗?您还可以使用 shmfs 或 tmpfs 重复测试以消除磁盘。您也可以尝试使用块设备调度程序。
  • 我尝试使用只分配一次的常量缓冲区,但我遇到了同样的问题。不过,你是对的,我对 tmpfs 没有任何问题,但是文件写入太快了,所以我将测试更改为多次写入相同的内容。所以这似乎是一个阻塞 I/O 事件的硬盘速度问题,但我不知道如何解决它。
  • 如果您必须使用常规文件,其中一个选项是将文件生成分成一个进程,而不是线程,这样您就可以更改它的 ionice(AFAIK 您不能更改线程 ionice)。用较低的离子生成它,它不会阻止请求的服务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多