非常令人惊讶的fprintf对比std :: ofstream(fprintf很慢)

时间:2011-10-24 14:53:28

标签: c++ ofstream printf

我正在运行一些基准测试,以找到一种最有效的方法,用C ++编写一个巨大的数组(超过1Go的ASCII)。

所以我将std :: ofstream与fprintf进行了比较(参见我下面使用的开关)

    case 0: {
        std::ofstream out(title, std::ios::out | std::ios::trunc);
        if (out) {
            ok = true;
            for (i=0; i<M; i++) {
                for (j=0; j<N; j++) {
                    out<<A[i][j]<<" ";
                }
                out<<"\n";
            }
            out.close();
        } else {
            std::cout<<"Error with file : "<<title<<"\n";
        }
        break;
    }
    case 1: {
        FILE *out = fopen(title.c_str(), "w");
        if (out!=NULL) {
            ok = true;
            for (i=0; i<M; i++) {
                for (j=0; j<N; j++) {
                    fprintf(out, "%d ", A[i][j]);
                }
                fprintf(out, "\n");
            }
            fclose(out);
        } else {
            std::cout<<"Error with file : "<<title<<"\n";
        }
        break;
    }

我的问题是,与std :: ofstream相比,fprintf似乎要慢12倍。您是否知道我的代码中问题的根源是什么?或者,与fprintf相比,std :: ofstream是非常优化的?

(和另一个问题:你知道另一种更快的写文件方式)

非常感谢

(详细信息:我正在使用g ++ -Wall -O3进行编译)

5 个答案:

答案 0 :(得分:17)

fprintf("%d"需要运行时解析格式字符串,每个整数一次。每次编译时,编译器会解析ostream& operator<<(ostream&, int)

答案 1 :(得分:4)

好吧,fprintf()必须在运行时做更多的工作,因为它必须解析和处理格式字符串。但是,考虑到输出文件的大小,我希望这些差异不会产生什么影响,并且会期望代码受I / O限制。

因此,我怀疑你的基准在某种程度上存在缺陷。

  1. 如果反复进行测试,你是否会一直得到12倍的差异?
  2. 如果您颠倒运行测试的顺序,时间会发生什么变化?
  3. 如果您最后致电fsync()/sync()会怎样?

答案 2 :(得分:2)

ofstream中有一个文件缓冲区,这可能会减少访问磁盘的时间。另外,fprintf是一个带有可变参数的函数,它会调用一些va_#函数,但是不能调用。我认为你可以使用fwrite()或putc()进行测试。

答案 3 :(得分:1)

您是否已将sync_with_stdio设置在您显示的代码的上游某处?

虽然你所报告的内容与实证所见的相反,但大多数人都认为并相信你所看到的应该是常态。 iostream是类型安全的,而printf系列函数是可变函数,必须从格式说明符推断出va_list的类型。

答案 4 :(得分:1)

我在这里介绍一种使用unix函数打开,读取和写入在文本文件上写入整数的非常优化的方法。它们也可以在Windows上使用,只需给你一些可以使用的警告。 此实现仅适用于32位整数。

在您的包含文件中:

class FastIntegerWriter
{
private:

    const int bufferSize;
    int offset;
    int file;
    char* buffer;

public:

    FastIntegerWriter(int bufferSize = 4096);
    int Open(const char *filename);
    void Close();
    virtual ~FastIntegerWriter();
    void Flush();
    void Writeline(int value);
};

在源文件中

#ifdef _MSC_VER
# include <io.h>
# define open _open
# define write _write
# define read _read
# define close _close
#else
# include <unistd.h>
#endif
#include <fcntl.h>

FastIntegerWriter::FastIntegerWriter(int bufferSize) :
    bufferSize(bufferSize),
    buffer(new char[bufferSize]),
    offset(0),
    file(0)
{
}

int FastIntegerWriter::Open(const char* filename)
{
    this->Close();
    if (filename != NULL)
        this->file = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
    return this->file;
}

void FastIntegerWriter::Close()
{
    this->Flush();
    if (this->file > 0)
    {
        close(this->file);
        this->file = 0;
    }
}

FastIntegerWriter::~FastIntegerWriter()
{
    this->Close();
    delete[] this->buffer;
}

void FastIntegerWriter::Flush()
{
    if (this->offset != 0)
    {
        write(this->file, this->buffer, this->offset);
        this->offset = 0;
    }
}

void FastIntegerWriter::Writeline(int value)
{
    if (this->offset >= this->bufferSize - 12)
    {
        this->Flush();
    }

    // Compute number of required digits

    char* output = this->buffer + this->offset;

    if (value < 0)
    {
        if (value == -2147483648)
        {
            // Special case, the minimum integer does not have a corresponding positive value.
            // We use an hard coded string and copy it directly to the buffer.
            // (Thanks to Eugene Ryabtsev for the suggestion).

            static const char s[] = "-2147483648\n";
            for (int i = 0; i < 12; ++i)
                output[i] = s[i];
            this->offset += 12;
            return;
        }

        *output = '-';
        ++output;
        ++this->offset;
        value = -value;
    }

    // Compute number of digits (log base 10(value) + 1)

    int digits =
        (value >= 1000000000) ? 10 : (value >= 100000000) ? 9 : (value >= 10000000) ? 8 : 
        (value >= 1000000) ? 7 : (value >= 100000) ? 6 : (value >= 10000) ? 5 : 
        (value >= 1000) ? 4 : (value >= 100) ? 3 : (value >= 10) ? 2 : 1;

    // Convert number to string

    output[digits] = '\n';
    for (int i = digits - 1; i >= 0; --i)
    {
        output[i] = value % 10 + '0';
        value /= 10;
    }

    this->offset += digits + 1;
}

我想这会比其他所有写入ascii文件的方法都要好:)你可以使用windows低级apis WriteFile和ReadFile获得更多性能,但它不值得付出努力。

使用它......

int main()
{
    FastIntegerWriter fw;
    fw.Open("test.txt");

    for (int i = -2000; i < 1000000; ++i)
        fw.Writeline(i);

    return 0;
}

如果您未指定任何文件,则使用标准输出(控制台)。