检查内存是不是清零的最快方法

Posted

技术标签:

【中文标题】检查内存是不是清零的最快方法【英文标题】:fastest way to check if memory is zeroed检查内存是否清零的最快方法 【发布时间】:2009-06-01 04:43:41 【问题描述】:

我有一个程序需要检查文件的一部分是否已清零或有数据。这个 alg 为整个文件运行,最多可以运行几场演出,并且需要一段时间才能运行。有没有更好的方法来检查它是否归零?

平台:Linux 和 windows

bool WGTController::isBlockCompleted(wgBlock* block)

    if (!block)
        return false;

    uint32 bufSize = (uint32)block->size;
    uint64 fileSize = UTIL::FS::UTIL_getFileSize(m_szFile);

    if (fileSize < (block->size + block->fileOffset))
        return false;

    char* buffer = new char[bufSize];

    FHANDLE fh=NULL;

    try
    
        fh = UTIL::FS::UTIL_openFile(m_szFile, UTIL::FS::FILE_READ);
        UTIL::FS::UTIL_seekFile(fh, block->fileOffset);
        UTIL::FS::UTIL_readFile(fh, buffer, bufSize);
        UTIL::FS::UTIL_closeFile(fh);
    
    catch (gcException &)
    
        SAFE_DELETEA(buffer);
        UTIL::FS::UTIL_closeFile(fh);
        return false;
    

    bool res = false;

    for (uint32 x=0; x<bufSize; x++)
    
        if (buffer[x] != 0)
        
            res = true;
            break;
        
    

    SAFE_DELETEA(buffer);
    return res;

【问题讨论】:

这个算法似乎有点“蛮力”。有没有一种好方法来确定接下来的 n 个字节是否有值?如果没有,这可能是您最好的选择(除了在您阅读文件时可能的文件争用)。 也许您的程序可以假设除非明确这样做,否则不会清除块?我的意思是,如果需要清除一个块,您的程序可以注意到这一点并清除数据(并注意该块是清除的)。我想我只是好奇你为什么要首先解决这个问题。 这里的“真正”问题是为什么首先需要扫描文件以查找零。也许你可以重新设计你的系统,这样就不需要这种无用的扫描,而不是试图提高它的速度。 【参考方案1】:

“一会儿”有多长? ...我会说尝试并行比较尽可能多的值会有所帮助,也许使用一些 SIMD 指令一次比较超过 4 个字节?

但请记住,无论您进行比较的速度有多快,最终仍需要从文件中读取数据。如果文件尚未在内存中某处的缓存中,则在磁盘带宽饱和之前,您可能会被限制在最大 100-150 MB/s 的数量级。如果您已经达到了这一点,那么您可能首先需要研究一种避免加载文件的方法,或者只是接受它不会比这更快的事实。

【讨论】:

hmmm,我可能会尝试将数组从 char 更改为 int,然后使用下面建议的 xor 方法。但是是的,文件读取时间是一个限制因素(这是恢复代码,即崩溃后)并且需要读取文件 在这种情况下,我会说,一旦你接近 8 秒/gig 左右,进一步的改进就会被磁盘带宽所淹没。 与执行 IO 所需的时间相比,循环内存和检查字节是否为 0 值所需的时间将是微不足道的。 CPU读取字节所花费的时间到底是什么。【参考方案2】:

文件/块中是否存在更有可能具有非零值的位置?您只需找到 一个 非零值(您的中断条件),因此首先查看您最有可能找到它们的位置 - 这不必是文件/块的开头。从最后开始,或者检查中间的 1/3 可能有意义,具体取决于实际应用。

但是,我不建议随机跳到不同的位置;从磁盘读取可能会变得难以置信;) ..

【讨论】:

【参考方案3】:

我想看看这个函数的汇编输出。 您可以做的可以大大加快速度的方法是使用 SSE 指令。使用这些指令,您可以一次加载 8 个字节,将它们全部检查为零并继续。 您也可以多次展开该 for 循环。

【讨论】:

【参考方案4】:

你的算法看起来不错,但是如果你事先知道你将获得什么类型的文件,你可以启发式地优化起始位置......但是如果它是一个特定的文件,很可能信息会在标题中(前几个字节)。

还要确保调用该方法的人的 block->size 不是 1 :)

还可以查看 Boost 的内存映射文件工具...这可能会有所帮助,具体取决于您如何计算最佳块->大小

【讨论】:

【参考方案5】:

我有一个“开箱即用”的答案,但我不确定在你的情况下实施它的可行性。

如果您不控制转储过程:由于它是在特殊情况下产生的大型恢复(转储?)文件,为什么不在转储后立即以低优先级扫描文件(0字节)并标记它以某种方式更快地识别? (或者您可以将其压缩并稍后解析/扫描 zip 文件)

或者,如果您控制转储过程:(无论如何您都必须执行一个缓慢的过程)为什么不在转储文件的末尾指出(或返回并在它的开头写入),如果转储文件已满有 0 还是有一些有效数据(因为你写了它并且你知道里面有什么)?这样您就不必为 I/O 开销支付两次费用。

这里的目标是通过将过程推迟到另一个更早的时间来加快读取速度,因为当转储发生时,不太可能有操作员等待它加载。

【讨论】:

这个文件中的数据是从网上分块下载的,不是按顺序下载的。因此,如果发生崩溃(或其停止并稍后恢复)需要确保数据在整个块中有效或重新下载。 但是您下载整个崩溃文件(以 chucnks 为单位)的唯一原因是检查它是否是零填充的?如果是这样,您不能在该机器(托管服务器)上运行监控进程,然后再进行查询以查看转储文件是否包含数据? 它不是一个崩溃文件,它是一个数据文件,块的原因是不需要所有文件,因此在我需要的块上下载。 好吧,然后检查 Boost 内存映射工具以加载数据块,如果可以确保数据没有损坏,也许让发送应用程序也将块的 CRC 发送给您.顺便说一句,使用 SSE 对您没有多大帮助,因为您的瓶颈实际上是 I/O。即使使用 byte ptr 扫描几 MB 内存也应该相当快。【参考方案6】:

首先,不要每次都分配新的缓冲区。分配一个(每个线程)并重用它。使用一个不错的大块,并进行多次读取/检查。 其次,不要比较每个字符。对更大的整数类型进行比较。您很可能需要 32 位 int,但取决于您的操作系统/编译器,使用 64 位甚至 128 位 int 可能会更快。使用 32 位 int,您可以将比较次数减少 4 倍。当然,您的遗嘱必须担心最终条件。为此,很容易,如果您要比较的缓冲区不是您的 int 大小的偶数倍,只需在进行比较之前将最后 X 个字节设置为 0。 第三,它可能会帮助您的编译器展开循环。在循环体中进行 4 或 8 次比较。这应该有助于编译器进行一些优化,并减少退出循环的比较次数。确保您的缓冲区是您的比较类型 x 循环中比较次数的倍数。 第四,使用 (*pBuffer++) 代替 buffer[i] 可能更快,尤其是在缓冲区很大的情况下。

对于其中任何一个,您当然希望获得一些指标,看看有什么实际帮助。

【讨论】:

【参考方案7】:

我会告诉你一个肮脏的、不可移植的和困难的方法,但比它更有效......如果你正在处理稀疏文件,你真的很无聊,想弄乱你的文件系统的内部结构'重新使用时,您可以尝试添加一个新函数,该函数返回一个位图,指示哪些块已映射,哪些未映射(未映射的块归零,其余部分仍需手动检查)。

是的,我知道这很疯狂,没有人愿意做这样的事情 xD

【讨论】:

以上是关于检查内存是不是清零的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

异步清零和同步置数/清零的区别

转载:2410中断中SRCPND和INTPND清零的疑问

C语言中,memset函数都可以给啥类型的数组清零?

free() 是不是将内存清零?

nohup.out追加日志的文件,文件太大自动清零的脚本

检查字节数组是不是全为零的最快方法