我们的日志记录类在初始化时会将日志文件截断为500,000字节。从那时起,日志语句将被附加到文件中。
我们这样做是为了保持较低的磁盘使用率,我们是一种商品最终用户产品。
显然,保留前500,000个字节是没有用的,所以我们保留最后500,000个字节。
我们的解决方案存在一些严重的性能问题。执行此操作的有效方法是什么?
发布于 2008-12-06 04:28:34
“我可能会创建一个新文件,在旧文件中查找,从旧文件到新文件执行缓冲读/写,将新文件重命名为旧文件。”
我觉得你最好干脆:
#include <fstream>
std::ifstream ifs("logfile"); //One call to start it all. . .
ifs.seekg(-512000, std::ios_base::end); // One call to find it. . .
char tmpBuffer[512000];
ifs.read(tmpBuffer, 512000); //One call to read it all. . .
ifs.close();
std::ofstream ofs("logfile", ios::trunc);
ofs.write(tmpBuffer, 512000); //And to the FS bind it.
通过简单地将最后的512K复制到缓冲区,以截断模式打开日志文件(清除日志文件的内容),并将相同的512K返回到文件的开头,这就避免了文件重命名的问题。
注意,上面的代码还没有经过测试,但我认为这个想法应该是合理的。
您可以将512K加载到内存的缓冲区中,关闭输入流,然后打开输出流;通过这种方式,您将不需要两个文件,因为您已经输入、关闭、打开、输出512字节,然后执行。通过这种方式,您可以避免重命名/文件重定位魔术。
如果您在某种程度上不反对将C与C++混合使用,您也可以这样做:
(注意:伪代码;我根本不记得mmap调用)
int myfd = open("mylog", O_RDONLY); // Grab a file descriptor
(char *) myptr = mmap(mylog, myfd, filesize - 512000) // mmap the last 512K
std::string mystr(myptr, 512000) // pull 512K from our mmap'd buffer and load it directly into the std::string
munmap(mylog, 512000); //Unmap the file
close(myfd); // Close the file descriptor
根据很多方面的情况,mmap可能比搜索更快。如果你感兴趣的话,可以在谷歌上搜索“fseek vs mmap”找到一些关于它的有趣读物。
HTH
发布于 2008-12-05 17:22:06
我可能会:
要完成前三个步骤(省略了错误检查,例如,如果文件小于500k,我就不记得seekg做了什么):
#include <fstream>
std::ifstream ifs("logfile");
ifs.seekg(-500*1000, std::ios_base::end);
std::ofstream ofs("logfile.new");
ofs << ifs.rdbuf();
然后我认为你必须做一些非标准的事情来重命名文件。
显然,您需要500k的空闲磁盘空间才能正常工作,因此,如果您截断日志文件的原因是因为它刚刚填满了磁盘,那么这是不好的。
我不确定为什么搜索速度很慢,所以我可能遗漏了什么。我不期望查找时间取决于文件的大小。可能取决于文件的是,我不确定这些函数是否可以处理32位系统上的2GB+文件。
如果复制本身很慢,那么根据平台的不同,您可以通过使用更大的缓冲区来加快复制速度,因为这可以减少系统调用的次数,也许更重要的是减少了磁头在读取点和写入点之间必须查找的次数。为此,请执行以下操作:
const int bufsize = 64*1024; // or whatever
std::vector<char> buf(bufsize);
...
ifs.rdbuf()->pubsetbuf(&buf[0], bufsize);
使用不同的值对其进行测试,并查看。您也可以尝试增加ofstream的缓冲区,我不确定这是否会有所不同。
请注意,在“实时”日志文件上使用我的方法是很麻烦的。例如,如果在副本和重命名之间附加了一个日志条目,那么您将永远丢失它,并且您试图替换的文件上的任何打开的句柄都可能导致问题(在Windows上它将失败,在linux上它将替换该文件,但旧的仍然会占用空间并且仍然会被写入,直到句柄关闭为止)。
如果截断是从执行所有日志记录的同一个线程完成的,那么就不会有任何问题,而且您可以保持简单。否则,您将需要使用锁或另一种方法。
这是否完全可靠取决于平台和文件系统:移动和替换可能是原子操作,也可能不是原子操作,但通常不是,因此您可能必须重命名旧文件,然后重命名新文件,然后删除旧文件,并进行错误恢复,在启动时检测是否有重命名的旧文件,如果有,则将其放回并重新启动截断。STL不能帮助您处理平台差异,但有boost::filesystem。
抱歉,这里有这么多警告,但很多都取决于平台。如果你使用的是PC,那么我不明白为什么复制一个微不足道的半兆数据需要花费任何时间。
发布于 2008-12-08 02:29:32
如果你碰巧用的是windows,那就别费心到处复制部件了。只需通过调用FSCTL_SET_SPARSE
和FSCTL_SET_ZERO_DATA
来告诉Windows您不再需要第一个字节
https://stackoverflow.com/questions/345610
复制相似问题