2011-10-24 19 views
7

Tôi đã chạy một số điểm chuẩn để tìm cách hiệu quả nhất để viết một mảng lớn vào một tệp trong C++ (hơn 1Go trong ASCII).Rất ngạc nhiên về số lượng fprintf vs std :: ofstream (fprintf rất chậm)

Vì vậy, tôi so std :: ofstream với fprintf (xem công tắc tôi đã sử dụng dưới đây)

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; 
    } 

Và vấn đề lớn của tôi là fprintf rằng có vẻ là Thant hơn 12x chậm hơn so với std :: ofstream. Bạn có ý tưởng về nguồn gốc của vấn đề trong mã của tôi không? Hoặc có thể std :: ofstream là rất tối ưu so với fprintf?

(và một câu hỏi khác: Bạn có biết một cách khác nhanh hơn để viết một tập tin)

Thank you very much

(chi tiết: Tôi đã được biên soạn với g ++ Wall-O3)

+0

tôi nghĩ bạn nên sử dụng fputs thay vì fprintf để có được hành vi tương tự hơn –

+1

cũng xem 'ostream :: write() ': http://www.cplusplus.com/reference/iostream/ostream/write/ – Nim

+1

@ AndersK .: Số fputs là tương đương với một streambuf (unformatted); fprintf là đối tác thích hợp của ostream. – MSalters

Trả lời

15

fprintf("%d" yêu cầu phân tích cú pháp thời gian chạy của chuỗi định dạng, một lần cho mỗi số nguyên. ostream& operator<<(ostream&, int) được giải quyết bởi trình biên dịch, một lần cho mỗi lần biên dịch.

1

Bạn đã đặt sync_with_stdio ở đâu đó ngược dòng mã bạn đã hiển thị chưa?

Trong khi những gì bạn báo cáo là trái ngược với những gì được chứng thực theo kinh nghiệm, hầu hết mọi người nghĩ và tin rằng những gì bạn thấy phải là chuẩn mực. iostreams là kiểu an toàn, trong khi họ printf của các hàm là các hàm variadic phải suy ra các kiểu của va_list từ định dạng specifier.

4

Vâng, fprintf() không phải làm nhiều hơn một chút khi chạy, vì nó phải phân tích cú pháp và xử lý chuỗi định dạng. Tuy nhiên, với kích thước của tập tin đầu ra của bạn, tôi sẽ mong đợi những khác biệt đó là do hậu quả nhỏ, và sẽ mong đợi mã được I/O bị ràng buộc.

Do đó, tôi nghi ngờ rằng điểm chuẩn của bạn là thiếu sót theo một cách nào đó.

  1. Bạn có thường xuyên nhận được sự khác biệt 12x nếu bạn chạy thử nghiệm liên tục không?
  2. Điều gì sẽ xảy ra với thời gian nếu bạn đảo ngược thứ tự mà bạn chạy thử nghiệm?
  3. Điều gì xảy ra nếu bạn gọi fsync()/sync() ở cuối?
2

Có bộ đệm tệp trong luồng, điều này có thể giảm thời gian truy cập vào đĩa. ngoài ra, fprintf là một hàm với các tham số biến sẽ gọi một số hàm va_ #, nhưng ngược lại sẽ không. Tôi nghĩ bạn có thể sử dụng hàm fwrite() hoặc putc() để thử nghiệm.

+0

putc sẽ chậm hơn vì nó chỉ viết một ký tự, nó sẽ chậm hơn. –

1

Tôi trình bày ở đây một cách tối ưu hóa thực sự để viết số nguyên trên một tệp văn bản bằng cách sử dụng các hàm unix mở, đọc và ghi. Họ cũng có sẵn trên các cửa sổ, chỉ cần cung cấp cho bạn một số cảnh báo bạn có thể làm việc với. Triển khai này chỉ hoạt động với số nguyên 32 bit.

Trong của bạn bao gồm file:

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); 
}; 

Trong file nguồn của bạn

#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; 
} 

Tôi đoán đây sẽ làm tốt hơn tất cả các phương pháp khác để viết thư cho một tập tin ascii :) bạn có thể nhận được một số hiệu suất hơn sử dụng Windows thấp cấp apis WriteFile và ReadFile, nhưng nó không có giá trị nỗ lực.

Để sử dụng nó ...

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

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

    return 0; 
} 

Nếu bạn không chỉ định bất kỳ tập tin nó sử dụng đầu ra tiêu chuẩn (console).

+1

Lưu ý rằng 'value = -value' sẽ hoạt động không chính xác nếu số nguyên âm nhất được chuyển vì không có giá trị dương tương ứng. Xem http://stackoverflow.com/a/5165813/1353187 –

+0

Đúng. Không nghĩ về việc viết mã đó. Cách tốt nhất và đơn giản nhất để xử lý nó là mã cứng trong một chuỗi số nguyên âm nhất và viết một if (value == most_negative_integer) write_the_string –