1

为什么 Java 中的这段代码比 C++ 快?我需要逐字节比较 2 个文件。例如,比较两个大小为 650mb 的文件时,C++ 需要 40 秒,Java 需要 10 秒。

C++ 代码:

//bufferSize = 8mb
std::ifstream lFile(lFilePath.c_str(), std::ios::in | std::ios::binary);
std::ifstream rFile(rFilePath.c_str(), std::ios::in | std::ios::binary);

std::streamsize lReadBytesCount = 0;
std::streamsize rReadBytesCount = 0;

do {
    lFile.read(p_lBuffer, *bufferSize);
    rFile.read(p_rBuffer, *bufferSize);
    lReadBytesCount = lFile.gcount();
    rReadBytesCount = rFile.gcount();

    if (lReadBytesCount != rReadBytesCount ||
        std::memcmp(p_lBuffer, p_rBuffer, lReadBytesCount) != 0)
    {
        return false;
    }
} while (lFile.good() || rFile.good());

return true;

和Java代码:

InputStream is1 = new BufferedInputStream(new FileInputStream(f1)); 
InputStream is2 = new BufferedInputStream(new FileInputStream(f2)); 

byte[] buffer1 = new byte[64];
byte[] buffer2 = new byte[64];

int readBytesCount1 = 0, readBytesCount2 = 0;

while (
    (readBytesCount1 = is1.read(buffer1)) != -1 &&
    (readBytesCount2 = is2.read(buffer2)) != -1
) {             
    if (Arrays.equals(buffer1, buffer2) && readBytesCount1 == readBytesCount2)
        countItr++;
    else {
        result = false
        break;
    }
}
4

3 回答 3

10

一个可能的答案是 C++ 代码使用 8 Mb 的缓冲区,而 Java 版本使用 64 字节。如果差异在前几个字节内会发生什么?那么Java版本只需要读取64个字节,就可以找到差异,而C++版本需要读取800万字节。如果您想比较它们,您应该使用相同的缓冲区大小。

此外,如果文件相同,则可能有其他原因造成差异。考虑分配 8 mb 数据所需的时间(这甚至可以跨越多个页面),与仅分配 64 字节所需的时间。由于您是按顺序阅读的,因此开销确实在内存方面。

于 2013-03-02T17:23:38.550 回答
1

我刚刚拿了你的 Java 程序并编写了一个等效的 C++ 程序,并且两者几乎相同来比较两个相同的文件,给或花一秒钟。

一种可能的、简单的解释是,您首先运行 C++ 程序,然后运行 ​​Java 程序。如果这是您唯一的测试,那么执行时间的差异可以通过缓存来解释,尽管在今天的硬件上读取 650 MB 需要 40 秒的时间。

数据块在系统文件缓存中,第二次没有磁盘访问来检索文件。为了获得可比较的结果,请使用 C++ 和 Java 程序多次运行测试。

在您的代码中,您有

lFile.read(p_lBuffer, *bufferSize);

这与您一开始的评论相矛盾

//bufferSize = 8mb

所以除非你展示真正完整的代码,否则任何人的猜测都是有效的。

吃我自己的狗粮

#include <iostream>
#include <fstream>
#include <cstring>

const size_t N = 8 * 1024 * 1024;
char buf1[N], buf2[N];

int main(int argc, char **argv)
{
    std::iostream::sync_with_stdio(false);
    std::ifstream f1(argv[1]);
    std::ifstream f2(argv[2]);
    while (f1.read(buf1, sizeof(buf1)) && f2.read(buf2, sizeof(buf2))) {
        size_t n1 = f1.gcount(), n2 = f2.gcount();
        if (n1 != n2 || memcmp(buf1, buf2, n1) != 0)
            return 1;
    }

    return 0;
}
于 2013-03-02T17:45:40.273 回答
0

虽然缓冲区大小的答案非常好,而且可能非常重要,但问题的另一个可能来源是使用iostream库。我通常不会将该库用于此类工作。例如,这可能导致的一个问题是额外的复制,因为iostream它为您提供了缓冲。我会使用原始readwrite调用。

例如,在 Linux C++11 平台上,我会这样做:

#include <array>
#include <algorithm>
#include <string>
#include <stdexcept>

// Needed for open and close on a Linux platform
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

using ::std::string;

bool same_contents(const string &fname1, const string &fname2)
{
   int fd1 = ::open(fname1.c_str(), O_RDONLY);
   if (fd1 < 0) {
      throw ::std::runtime_error("Open of " + fname1 + " failed.");
   }
   int fd2 = ::open(fname2.c_str(), O_RDONLY);
   if (fd2 < 0) {
      ::close(fd1);
      fd1 = -1;
      throw ::std::runtime_error("Open of " + fname2 + " failed.");
   }

   bool same = true;
   try {
      ::std::array<char, 4096> buf1;
      ::std::array<char, 4096> buf2;
      bool done = false;

      while (!done) {
         int read1 = ::read(fd1, buf1.data(), buf1.size());
         if (read1 < 0) {
            throw ::std::runtime_error("Error reading " + fname1);
         }
         int read2 = ::read(fd2, buf2.data(), buf2.size());
         if (read2 < 0) {
            throw ::std::runtime_error("Error reading " + fname2);
         }
         if (read1 != read2) {
            same = false;
            done = true;
         }
         if (same && read1 > 0) {
            const auto compare_result = ::std::mismatch(buf1.begin(),
                                                        buf1.begin() + read1,
                                                        buf2.begin());
            if (compare_result.first != (buf1.begin() + read1)) {
               same = false;
            }
         }
         if (!same || (buf1.size() > read1)) {
            done = true;
         }
      }
   } catch (...) {
      if (fd1 >= 0) ::close(fd1);
      if (fd2 >= 0) ::close(fd2);
      throw;
   }
   if (fd1 >= 0) ::close(fd1);
   if (fd2 >= 0) ::close(fd2);
   return same;
}
于 2013-03-02T17:30:19.850 回答