我在编写文件时遇到了一些问题 - 即无法写得足够快。
为了解释,我的目标是捕获通过千兆以太网传输的数据流,并将其保存到文件中。
原始数据以10MS / s的速率进入,然后将其保存到缓冲区并随后写入文件。
以下是相关的代码部分:
std::string path = "Stream/raw.dat";
ofstream outFile(path, ios::out | ios::app| ios::binary);
if(outFile.is_open())
cout << "Yes" << endl;
while(1)
{
rxSamples = rxStream->recv(&rxBuffer[0], rxBuffer.size(), metaData);
switch(metaData.error_code)
{
//Irrelevant error checking...
//Write data to a file
std::copy(begin(rxBuffer), end(rxBuffer), std::ostream_iterator<complex<float>>(outFile));
}
}
我遇到的问题是将样本写入文件花了太长时间。大约一秒钟后,发送样本的设备报告其缓冲区已溢出。在对代码进行一些快速分析之后,几乎所有的执行时间都花费在std::copy(...)
上(确切地说是99.96%)。如果我删除这一行,我可以运行该程序几个小时而不会遇到任何溢出。
那就是说,我对如何提高写入速度感到非常难过。我已经浏览了这个网站上的几个帖子,似乎最常见的建议(关于速度)是通过使用std::copy
来实现我已经完成的文件写入。
如果它有用,我在Ubuntu x86_64上运行此程序。任何建议将不胜感激。
答案 0 :(得分:13)
所以这里的主要问题是你尝试在收到的同一个线程中写入,这意味着你的recv()只能在复制完成后再次调用。一些观察结果:
事实上,我不知道你是如何想出std::copy
方法的。 rx_samples_to_file example that comes with UHD用简单的写法做到这一点,你绝对应该赞成复制;在优秀的操作系统上,文件I / O通常可以减少一个副本,并且迭代所有元素可能非常慢。
答案 1 :(得分:4)
让我们做一些数学运算。
您的样本(显然)属于std::complex<std::float>
类型。给定(典型的)32位浮点数,这意味着每个样本是64位。在10 MS / s时,这意味着原始数据大约是每秒80兆字节 - 这可以达到您可以期望写入桌面(7200 RPM)硬盘的范围,但是接近极限(通常大约为100)每秒-100兆字节左右。)
不幸的是,尽管std::ios::binary
,您实际上是以文本格式编写数据(因为std::ostream_iterator
基本上是stream << data;
)。
这不仅会失去一些精确度,而且会增加数据的大小,至少通常是这样。确切的增加量取决于数据 - 小的整数值实际上可以减少数据量,但对于任意输入,大小增加接近2:1是相当普遍的。随着2:1的增加,您的传出数据现在大约为160兆字节/秒 - 这比大多数硬盘驱动器可以处理的速度快。
改进的明显起点是以二进制格式编写数据:
uint32_t nItems = std::end(rxBuffer)-std::begin(rxBuffer);
outFile.write((char *)&nItems, sizeof(nItems));
outFile.write((char *)&rxBuffer[0], sizeof(rxBuffer));
目前我使用sizeof(rxBuffer)
假设它是一个真正的数组。如果它实际上是指针或向量,则必须计算正确的大小(您想要的是要写入的总字节数)。
我还注意到,就目前看来,你的代码有一个更严重的问题:因为它在写入数据时没有在元素之间指定分隔符,所以数据将被写入而没有任何东西来分隔来自下一个的项目。这意味着如果你写了两个(例如)1
和0.2
的值,那么你读回的内容将不是1
和0.2
,而是一个值10.2
。将分隔符添加到文本输出中会为已经失败的进程增加更多开销(大约多15%的数据),因为它会生成太多数据。
以二进制格式写入意味着每个浮点数将精确消耗4个字节,因此不需要分隔符来正确读取数据。
之后的下一步是下降到较低级别的文件I / O例程。根据具体情况,这可能会或可能不会产生太大影响。在Windows上,您可以在使用FILE_FLAG_NO_BUFFERING
打开文件时指定CreateFile
。这意味着对该文件的读写将基本绕过缓存并直接进入磁盘。
在你的情况下,这可能是一个胜利 - 在10 MS / s时,你可能会在重读相同数据之前花费一段时间来使用缓存空间。在这种情况下,让数据进入缓存几乎不会带来任何好处,但是会花费一些数据将数据复制到缓存,然后稍后将其复制到磁盘。更糟糕的是,它可能会使用所有这些数据污染缓存,因此它不再存储更有可能从缓存中受益的其他数据。