Viết một tệp nhị phân trong C ++ rất nhanh


240

Tôi đang cố gắng ghi một lượng lớn dữ liệu vào ổ SSD của mình (ổ đĩa trạng thái rắn). Và với số tiền rất lớn, ý tôi là 80GB.

Tôi duyệt web để tìm giải pháp, nhưng điều tốt nhất tôi nghĩ ra là:

#include <fstream>
const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
    std::fstream myfile;
    myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    //Here would be some error handling
    for(int i = 0; i < 32; ++i){
        //Some calculations to fill a[]
        myfile.write((char*)&a,size*sizeof(unsigned long long));
    }
    myfile.close();
}

Được biên dịch với Visual Studio 2010 và tối ưu hóa đầy đủ và chạy trong Windows7, chương trình này đạt tối đa khoảng 20MB / s. Điều thực sự làm phiền tôi là Windows có thể sao chép các tệp từ một ổ SSD khác sang ổ SSD này ở khoảng từ 150MB / s đến 200MB / s. Vì vậy, nhanh hơn ít nhất 7 lần. Đó là lý do tại sao tôi nghĩ rằng tôi sẽ có thể đi nhanh hơn.

Bất kỳ ý tưởng làm thế nào tôi có thể tăng tốc độ viết của tôi?


11
Có phải kết quả thời gian của bạn đã loại trừ thời gian cần thiết để tính toán để điền vào []?
Catchmeifyoutry

7
Tôi thực sự đã làm nhiệm vụ này trước đây. Sử dụng đơn giản fwrite()tôi có thể nhận được khoảng 80% tốc độ ghi cao điểm. Chỉ với FILE_FLAG_NO_BUFFERINGtôi đã bao giờ có thể có được tốc độ tối đa.
Bí ẩn

10
Tôi không chắc chắn việc so sánh việc ghi tệp của bạn với sao chép SSD sang SSD. Cũng có thể là SSD-to-SSD hoạt động ở mức thấp hơn, tránh các thư viện C ++ hoặc sử dụng truy cập bộ nhớ trực tiếp (DMA). Sao chép một cái gì đó không giống như viết các giá trị tùy ý vào một tệp truy cập ngẫu nhiên.
Igor F.

4
@IgorF.: Đó chỉ là suy đoán sai lầm; đó là một so sánh hoàn toàn công bằng (nếu không có gì khác, có lợi cho việc viết tệp). Sao chép trên một ổ đĩa trong Windows chỉ là đọc và ghi; không có gì lạ mắt / phức tạp / khác nhau đang diễn ra bên dưới.
dùng541686

5
@MaximYegorushkin: Liên kết hoặc nó đã không xảy ra. : P
dùng541686

Câu trả lời:


232

Điều này đã làm công việc (trong năm 2012):

#include <stdio.h>
const unsigned long long size = 8ULL*1024ULL*1024ULL;
unsigned long long a[size];

int main()
{
    FILE* pFile;
    pFile = fopen("file.binary", "wb");
    for (unsigned long long j = 0; j < 1024; ++j){
        //Some calculations to fill a[]
        fwrite(a, 1, size*sizeof(unsigned long long), pFile);
    }
    fclose(pFile);
    return 0;
}

Tôi chỉ hẹn giờ 8GB trong 36 giây, tốc độ khoảng 220 MB / giây và tôi nghĩ rằng tối đa hóa SSD của mình. Cũng đáng lưu ý, mã trong câu hỏi đã sử dụng một lõi 100%, trong khi mã này chỉ sử dụng 2-5%.

Cảm ơn mọi người rất nhiều.

Cập nhật : 5 năm đã trôi qua 2017. Trình biên dịch, phần cứng, thư viện và yêu cầu của tôi đã thay đổi. Đó là lý do tại sao tôi thực hiện một số thay đổi đối với mã và thực hiện một số phép đo mới.

Đầu tiên lên mã:

#include <fstream>
#include <chrono>
#include <vector>
#include <cstdint>
#include <numeric>
#include <random>
#include <algorithm>
#include <iostream>
#include <cassert>

std::vector<uint64_t> GenerateData(std::size_t bytes)
{
    assert(bytes % sizeof(uint64_t) == 0);
    std::vector<uint64_t> data(bytes / sizeof(uint64_t));
    std::iota(data.begin(), data.end(), 0);
    std::shuffle(data.begin(), data.end(), std::mt19937{ std::random_device{}() });
    return data;
}

long long option_1(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    auto startTime = std::chrono::high_resolution_clock::now();
    auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    myfile.write((char*)&data[0], bytes);
    myfile.close();
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

long long option_2(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    auto startTime = std::chrono::high_resolution_clock::now();
    FILE* file = fopen("file.binary", "wb");
    fwrite(&data[0], 1, bytes, file);
    fclose(file);
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

long long option_3(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    std::ios_base::sync_with_stdio(false);
    auto startTime = std::chrono::high_resolution_clock::now();
    auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    myfile.write((char*)&data[0], bytes);
    myfile.close();
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

int main()
{
    const std::size_t kB = 1024;
    const std::size_t MB = 1024 * kB;
    const std::size_t GB = 1024 * MB;

    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option1, " << size / MB << "MB: " << option_1(size) << "ms" << std::endl;
    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option2, " << size / MB << "MB: " << option_2(size) << "ms" << std::endl;
    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option3, " << size / MB << "MB: " << option_3(size) << "ms" << std::endl;

    return 0;
}

Mã này biên dịch với Visual Studio 2017 và g ++ 7.2.0 (một yêu cầu mới). Tôi đã chạy mã với hai thiết lập:

  • Máy tính xách tay, Core i7, SSD, Ubuntu 16.04, g ++ Phiên bản 7.2.0 với -std = c ++ 11 -march = bản địa -O3
  • Máy tính để bàn, Core i7, SSD, Windows 10, Visual Studio 2017 Phiên bản 15.3.1 với / Ox / Ob2 / Oi / Ot / GT / GL / Gy

Việc đưa ra các phép đo sau (sau khi bỏ các giá trị trong 1MB, vì chúng là các ngoại lệ rõ ràng): Cả hai lần tùy chọn1 và tùy chọn3 đều tối đa hóa SSD của tôi. Tôi không mong đợi điều này sẽ thấy, bởi vì tùy chọn 2 trước đây là mã nhanh nhất trên máy cũ của tôi.nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

TL; DR : Các phép đo của tôi cho thấy sử dụng std::fstreamhơn FILE.


8
+1 Vâng, đây là điều đầu tiên tôi thử. FILE*nhanh hơn luồng. Tôi sẽ không mong đợi một sự khác biệt như vậy vì dù sao nó "nên" bị ràng buộc I / O.
Bí ẩn

12
Chúng ta có thể kết luận rằng I / O kiểu C nhanh hơn nhiều so với luồng C ++ không?
SChepurin

21
@SChepurin: Nếu bạn là người phạm tội, có lẽ là không. Nếu bạn đang thực tế, có lẽ là có. :)
dùng541686

10
Bạn có thể giải thích (đối với một công ty C ++ như tôi) về sự khác biệt giữa hai cách tiếp cận và tại sao phương pháp này hoạt động nhanh hơn nhiều so với bản gốc?
Mike Chamberlain

11
Việc chuẩn bị có ios::sync_with_stdio(false);tạo ra sự khác biệt nào cho mã với luồng không? Tôi chỉ tò mò về sự khác biệt lớn giữa việc sử dụng dòng này và không, nhưng tôi không có đĩa đủ nhanh để kiểm tra trường hợp góc. Và nếu có bất kỳ sự khác biệt thực sự.
Artur Czajka

24

Hãy thử các cách sau, theo thứ tự:

  • Kích thước bộ đệm nhỏ hơn. Viết ~ 2 MiB tại một thời điểm có thể là một khởi đầu tốt. Trên máy tính xách tay cuối cùng của tôi, ~ 512 KiB là điểm hấp dẫn, nhưng tôi chưa thử nghiệm trên SSD của mình.

    Lưu ý: Tôi đã nhận thấy rằng bộ đệm rất lớn có xu hướng giảm hiệu suất. Tôi đã nhận thấy sự mất tốc độ khi sử dụng bộ đệm 16-MiB thay vì bộ đệm 512-KiB trước đây.

  • Sử dụng _open(hoặc _topennếu bạn muốn chính xác Windows) để mở tệp, sau đó sử dụng _write. Điều này có thể sẽ tránh được rất nhiều bộ đệm, nhưng nó không chắc chắn.

  • Sử dụng các chức năng dành riêng cho Windows như CreateFileWriteFile. Điều đó sẽ tránh bất kỳ bộ đệm trong thư viện tiêu chuẩn.


Kiểm tra bất kỳ kết quả điểm chuẩn được đăng trực tuyến. Bạn cần ghi 4kB với độ sâu hàng đợi từ 32 trở lên, hoặc ghi 512K trở lên, để có được bất kỳ loại thông lượng tốt nào.
Ben Voigt

@BenVoigt: Yup, điều đó tương quan với tôi khi nói 512 KiB là điểm ngọt ngào đối với tôi. :)
dùng541686

Đúng. Từ kinh nghiệm của tôi, kích thước bộ đệm nhỏ hơn thường là tối ưu. Ngoại lệ là khi bạn đang sử dụng FILE_FLAG_NO_BUFFERING- trong đó bộ đệm lớn hơn có xu hướng tốt hơn. Vì tôi nghĩ FILE_FLAG_NO_BUFFERINGlà khá nhiều DMA.
Bí ẩn

22

Tôi thấy không có sự khác biệt giữa std :: stream / FILE / thiết bị. Giữa đệm và không đệm.

Cũng lưu ý:

  • Ổ SSD "có xu hướng" chậm lại (tốc độ truyền thấp hơn) khi chúng đầy.
  • Ổ SSD "có xu hướng" chậm lại (tốc độ truyền thấp hơn) khi chúng cũ hơn (vì các bit không hoạt động).

Tôi đang thấy mã chạy trong 63 giây.
Do đó, tốc độ truyền tải là: 260M / s (SSD của tôi trông nhanh hơn một chút so với của bạn).

64 * 1024 * 1024 * 8 /*sizeof(unsigned long long) */ * 32 /*Chunks*/

= 16G
= 16G/63 = 260M/s

Tôi không tăng thêm bằng cách chuyển sang TẬP_TIN * từ std :: fux.

#include <stdio.h>

using namespace std;

int main()
{
    
    FILE* stream = fopen("binary", "w");

    for(int loop=0;loop < 32;++loop)
    {
         fwrite(a, sizeof(unsigned long long), size, stream);
    }
    fclose(stream);

}

Vì vậy, luồng C ++ đang hoạt động nhanh như thư viện cơ bản sẽ cho phép.

Nhưng tôi nghĩ thật không công bằng khi so sánh HĐH với một ứng dụng được xây dựng trên đỉnh của HĐH. Ứng dụng có thể không đưa ra giả định (không biết các ổ đĩa là SSD) và do đó sử dụng các cơ chế tệp của HĐH để chuyển.

Trong khi HĐH không cần đưa ra bất kỳ giả định nào. Nó có thể cho biết các loại ổ đĩa liên quan và sử dụng kỹ thuật tối ưu để truyền dữ liệu. Trong trường hợp này một bộ nhớ trực tiếp để chuyển bộ nhớ. Hãy thử viết một chương trình sao chép 80G từ 1 vị trí trong bộ nhớ sang vị trí khác và xem tốc độ đó nhanh như thế nào.

Biên tập

Tôi đã thay đổi mã của mình để sử dụng các cuộc gọi cấp thấp hơn:
tức là không có bộ đệm.

#include <fcntl.h>
#include <unistd.h>


const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
    int data = open("test", O_WRONLY | O_CREAT, 0777);
    for(int loop = 0; loop < 32; ++loop)
    {   
        write(data, a, size * sizeof(unsigned long long));
    }   
    close(data);
}

Điều này làm cho không có sự khác biệt.

LƯU Ý : Ổ đĩa của tôi là ổ SSD nếu bạn có ổ đĩa bình thường, bạn có thể thấy sự khác biệt giữa hai kỹ thuật trên. Nhưng như tôi mong đợi không đệm và đệm (khi viết các đoạn lớn lớn hơn kích thước bộ đệm) sẽ không có sự khác biệt.

Chỉnh sửa 2:

Bạn đã thử phương pháp sao chép tệp nhanh nhất trong C ++ chưa

int main()
{
    std::ifstream  input("input");
    std::ofstream  output("ouptut");

    output << input.rdbuf();
}

5
Tôi đã không downvote, nhưng kích thước bộ đệm của bạn quá nhỏ. Tôi đã làm điều đó với cùng bộ đệm 512 MB mà OP đang sử dụng và tôi nhận được 20 MB / s với các luồng so với 90 MB / s với FILE*.
Bí ẩn

Cũng theo cách của bạn với fwrite (a, sizeof (dài không dấu), kích thước, luồng); thay vì fwrite (a, 1, size * sizeof (dài không dấu), pFile); mang lại cho tôi 220MB / s với số lượng 64 MB mỗi lần ghi.
Dominic Hofer

2
@Mysticial: Điều ngạc nhiên là kích thước bộ đệm của tôi tạo ra sự khác biệt (mặc dù tôi tin bạn). Bộ đệm rất hữu ích khi bạn có nhiều ghi nhỏ để thiết bị bên dưới không bị làm phiền với nhiều yêu cầu. Nhưng khi bạn đang viết các đoạn lớn, không cần bộ đệm khi viết / đọc (trên thiết bị chặn). Do đó, dữ liệu phải được truyền trực tiếp đến thiết bị bên dưới (do đó bằng cách chuyển qua bộ đệm). Mặc dù nếu bạn thấy một sự khác biệt, điều này sẽ mâu thuẫn với điều này và khiến tôi tự hỏi tại sao bài viết thực sự sử dụng bộ đệm.
Martin York

2
Giải pháp tốt nhất là KHÔNG tăng kích thước bộ đệm mà là loại bỏ bộ đệm và ghi dữ liệu trực tiếp vào thiết bị bên dưới.
Martin York

1
@Mysticial: 1) Không có khối nhỏ => Nó luôn đủ lớn (trong ví dụ này). Trong trường hợp này, khối là 512M 2) Đây là ổ SSD (cả của tôi và OP) vì vậy không có cái nào phù hợp. Tôi đã cập nhật câu trả lời của tôi.
Martin York

13

Giải pháp tốt nhất là thực hiện viết không đồng bộ với bộ đệm đôi.

Nhìn vào dòng thời gian:

------------------------------------------------>
FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|

'F' biểu thị thời gian để điền vào bộ đệm và 'W' biểu thị thời gian ghi bộ đệm vào đĩa. Vì vậy, vấn đề lãng phí thời gian giữa việc viết bộ đệm vào tập tin. Tuy nhiên, bằng cách thực hiện viết trên một luồng riêng biệt, bạn có thể bắt đầu điền vào bộ đệm tiếp theo ngay sau đây:

------------------------------------------------> (main thread, fills buffers)
FF|ff______|FF______|ff______|________|
------------------------------------------------> (writer thread)
  |WWWWWWWW|wwwwwwww|WWWWWWWW|wwwwwwww|

F - điền vào bộ đệm thứ nhất
f - điền vào bộ đệm thứ 2
W - ghi bộ đệm thứ nhất vào tệp
w - ghi bộ đệm thứ 2 vào tệp
_ - chờ trong khi thao tác hoàn tất

Cách tiếp cận này với hoán đổi bộ đệm rất hữu ích khi điền vào bộ đệm đòi hỏi tính toán phức tạp hơn (do đó, mất nhiều thời gian hơn). Tôi luôn triển khai lớp CSequentialStreamWriter ẩn cách viết không đồng bộ bên trong, vì vậy đối với người dùng cuối, giao diện chỉ có (các) chức năng Viết.

Và kích thước bộ đệm phải là bội số của kích thước cụm đĩa. Nếu không, bạn sẽ kết thúc với hiệu suất kém bằng cách ghi một bộ đệm vào 2 cụm đĩa liền kề.

Viết bộ đệm cuối cùng.
Khi bạn gọi hàm Write lần cuối, bạn phải chắc chắn rằng bộ đệm hiện tại đang được điền cũng nên được ghi vào đĩa. Do đó, CSequentialStreamWriter nên có một phương thức riêng, giả sử Finalize (bộ đệm cuối cùng), ghi vào đĩa cuối cùng của dữ liệu.

Xử lý lỗi.
Trong khi mã bắt đầu điền vào bộ đệm thứ 2 và mã thứ nhất đang được viết trên một luồng riêng biệt, nhưng viết không thành công vì một số lý do, luồng chính phải nhận thức được sự thất bại đó.

------------------------------------------------> (main thread, fills buffers)
FF|fX|
------------------------------------------------> (writer thread)
__|X|

Giả sử giao diện của CSequentialStreamWriter có hàm Write trả về bool hoặc ném một ngoại lệ, do đó có lỗi trên một luồng riêng biệt, bạn phải nhớ trạng thái đó, vì vậy, lần tới khi bạn gọi Write hoặc Finilize trên luồng chính, phương thức sẽ trả về Sai hoặc sẽ ném một ngoại lệ. Và việc bạn dừng điền bộ đệm vào thời điểm nào không quan trọng, ngay cả khi bạn đã viết một số dữ liệu trước khi thất bại - rất có thể tệp sẽ bị hỏng và vô dụng.


3
Thực hiện I / O song song với các tính toán là một ý tưởng rất hay, nhưng trên Windows, bạn không nên sử dụng các luồng để thực hiện nó. Thay vào đó, hãy sử dụng "I / O chồng chéo", không chặn một trong các chuỗi của bạn trong cuộc gọi I / O. Điều đó có nghĩa là bạn hầu như không phải lo lắng về việc đồng bộ hóa luồng (chỉ cần không truy cập vào bộ đệm có hoạt động I / O hoạt động bằng cách sử dụng nó).
Ben Voigt

11

Tôi khuyên bạn nên thử ánh xạ tệp . Tôi đã sử dụng mmaptrong quá khứ, trong môi trường UNIX và tôi rất ấn tượng bởi hiệu suất cao mà tôi có thể đạt được


1
@nalply Đây vẫn là một giải pháp hiệu quả, hiệu quả và thú vị cần ghi nhớ.
Yam Marcovic

stackoverflow.com/a/2895799/220060 về ưu điểm của mmap. Đặc biệt lưu ý "Đối với truy cập tuần tự thuần túy vào tệp, nó cũng không phải luôn là giải pháp tốt hơn [...]" Ngoài ra stackoverflow.com/questions/726471 , nó thực sự nói rằng trên hệ thống 32 bit, bạn bị giới hạn ở 2 hoặc 3 GB. - nhân tiện, không phải tôi là người hạ thấp câu trả lời đó.
nalply

8

Bạn có thể sử dụng FILE*thay thế, và đo lường hiệu suất bạn đã đạt được? Một vài lựa chọn là sử dụng fwrite/writethay vì fstream:

#include <stdio.h>

int main ()
{
  FILE * pFile;
  char buffer[] = { 'x' , 'y' , 'z' };
  pFile = fopen ( "myfile.bin" , "w+b" );
  fwrite (buffer , 1 , sizeof(buffer) , pFile );
  fclose (pFile);
  return 0;
}

Nếu bạn quyết định sử dụng write, hãy thử một cái gì đó tương tự:

#include <unistd.h>
#include <fcntl.h>

int main(void)
{
    int filedesc = open("testfile.txt", O_WRONLY | O_APPEND);

    if (filedesc < 0) {
        return -1;
    }

    if (write(filedesc, "This will be output to testfile.txt\n", 36) != 36) {
        write(2, "There was an error writing to testfile.txt\n", 43);
        return -1;
    }

    return 0;
}

Tôi cũng sẽ khuyên bạn nên xem xét memory map. Đó có thể là câu trả lời của bạn. Khi tôi phải xử lý một tệp 20 GB khác để lưu trữ nó trong cơ sở dữ liệu và tệp này thậm chí không mở. Vì vậy, các giải pháp như sử dụng bản đồ moemory. Tôi đã làm điều đó Pythonmặc dù.


Trên thực tế, một FILE*mã tương đương thẳng với mã gốc sử dụng cùng bộ đệm 512 MB có tốc độ tối đa. Bộ đệm hiện tại của bạn quá nhỏ.
Bí ẩn

1
@Mysticial Nhưng đó chỉ là một ví dụ.
cybertextron

Trong hầu hết các hệ thống, 2tương ứng với lỗi tiêu chuẩn nhưng chúng tôi vẫn khuyên bạn nên sử dụng STDERR_FILENOthay vì 2. Một vấn đề quan trọng khác là một lỗi có thể xảy ra mà bạn có thể gặp là EINTR khi bạn nhận được tín hiệu ngắt, đây không phải là lỗi thực sự và bạn chỉ cần thử lại.
Peyman

6

Hãy thử sử dụng các lệnh gọi API open () / write () / close () và thử nghiệm với kích thước bộ đệm đầu ra. Ý tôi là không vượt qua toàn bộ bộ đệm "nhiều-byte" cùng một lúc, hãy thực hiện một vài lần ghi (ví dụ: TotalNumBytes / OutBufferSize). OutBufferSize có thể từ 4096 byte đến megabyte.

Một lần thử khác - sử dụng WinAPI OpenFile / CreateFile và sử dụng bài viết MSDN này để tắt bộ đệm (FILE_FLAG_NO_BUFFITH). Và bài viết MSDN này trên WriteFile () chỉ ra cách lấy kích thước khối cho ổ đĩa để biết kích thước bộ đệm tối ưu.

Dù sao, std :: ofstream là một trình bao bọc và có thể có các hoạt động I / O bị chặn. Hãy nhớ rằng việc duyệt qua toàn bộ mảng N-gigabyte cũng mất một thời gian. Trong khi bạn đang viết một bộ đệm nhỏ, nó sẽ vào bộ đệm và hoạt động nhanh hơn.


6

fstreams không chậm hơn các luồng C, mỗi se, nhưng chúng sử dụng nhiều CPU hơn (đặc biệt là nếu bộ đệm không được cấu hình đúng cách). Khi CPU bão hòa, nó giới hạn tốc độ I / O.

Ít nhất là triển khai MSVC 2015 sao chép 1 char tại một thời điểm vào bộ đệm đầu ra khi bộ đệm luồng không được đặt (xem streambuf::xsputn). Vì vậy, hãy đảm bảo đặt bộ đệm luồng (> 0) .

Tôi có thể nhận tốc độ ghi 1500MB / s (tốc độ tối đa của SSD M.2 của tôi) bằng fstreamcách sử dụng mã này:

#include <iostream>
#include <fstream>
#include <chrono>
#include <memory>
#include <stdio.h>
#ifdef __linux__
#include <unistd.h>
#endif
using namespace std;
using namespace std::chrono;
const size_t sz = 512 * 1024 * 1024;
const int numiter = 20;
const size_t bufsize = 1024 * 1024;
int main(int argc, char**argv)
{
  unique_ptr<char[]> data(new char[sz]);
  unique_ptr<char[]> buf(new char[bufsize]);
  for (size_t p = 0; p < sz; p += 16) {
    memcpy(&data[p], "BINARY.DATA.....", 16);
  }
  unlink("file.binary");
  int64_t total = 0;
  if (argc < 2 || strcmp(argv[1], "fopen") != 0) {
    cout << "fstream mode\n";
    ofstream myfile("file.binary", ios::out | ios::binary);
    if (!myfile) {
      cerr << "open failed\n"; return 1;
    }
    myfile.rdbuf()->pubsetbuf(buf.get(), bufsize); // IMPORTANT
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      myfile.write(data.get(), sz);
      if (!myfile)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    myfile.close();
  }
  else {
    cout << "fopen mode\n";
    FILE* pFile = fopen("file.binary", "wb");
    if (!pFile) {
      cerr << "open failed\n"; return 1;
    }
    setvbuf(pFile, buf.get(), _IOFBF, bufsize); // NOT important
    auto tm1 = high_resolution_clock::now();
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      if (fwrite(data.get(), sz, 1, pFile) != 1)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    fclose(pFile);
    auto tm2 = high_resolution_clock::now();
  }
  cout << "Total: " << total << " ms, " << (sz*numiter * 1000 / (1024.0 * 1024 * total)) << " MB/s\n";
}

Tôi đã thử mã này trên các nền tảng khác (Ubuntu, FreeBSD) và nhận thấy không có sự khác biệt về tốc độ I / O, nhưng chênh lệch sử dụng CPU khoảng 8: 1 ( fstreamsử dụng CPU nhiều hơn 8 lần ). Vì vậy, người ta có thể tưởng tượng, nếu tôi có một đĩa nhanh hơn, việc fstreamghi sẽ chậm lại sớm hơn stdiophiên bản.


3

Cố gắng sử dụng các tập tin ánh xạ bộ nhớ.


@Mehrdad nhưng tại sao? Bởi vì đó là một giải pháp phụ thuộc nền tảng?
qehgt

3
Không ... đó là vì để thực hiện ghi tập tin tuần tự nhanh, bạn cần phải viết một lượng lớn dữ liệu cùng một lúc. (Giả sử, các khối 2-MiB có thể là điểm khởi đầu tốt.) Các tệp được ánh xạ bộ nhớ không cho phép bạn kiểm soát mức độ chi tiết, do đó, bạn rất tự hào về bất cứ điều gì mà trình quản lý bộ nhớ quyết định tìm nạp trước / bộ đệm cho bạn. Nói chung, tôi chưa bao giờ thấy chúng hiệu quả như đọc / viết thông thường ReadFilevà như vậy đối với truy cập tuần tự, mặc dù đối với truy cập ngẫu nhiên, chúng có thể tốt hơn.
dùng541686

Nhưng các tệp ánh xạ bộ nhớ được OS sử dụng để phân trang chẳng hạn. Tôi nghĩ rằng đó là một cách tối ưu hóa (về tốc độ) để đọc / ghi dữ liệu.
qehgt

7
@Mysticial: Mọi người 'biết "rất nhiều điều hoàn toàn sai.
Ben Voigt

1
@qehgt: Nếu có bất cứ điều gì, phân trang được tối ưu hóa nhiều hơn cho truy cập ngẫu nhiên so với truy cập tuần tự. Đọc 1 trang dữ liệu chậm hơn nhiều so với đọc 1 megabyte dữ liệu trong một thao tác.
dùng541686

3

Nếu bạn sao chép thứ gì đó từ đĩa A sang đĩa B trong explorer, Windows sẽ sử dụng DMA. Điều đó có nghĩa là đối với hầu hết quá trình sao chép, CPU về cơ bản sẽ không làm gì khác ngoài việc nói cho bộ điều khiển đĩa biết nơi đặt và lấy dữ liệu từ đó, loại bỏ toàn bộ một bước trong chuỗi và một bước không được tối ưu hóa cho việc di chuyển số lượng lớn dữ liệu - và ý tôi là phần cứng.

Những gì bạn làm liên quan đến CPU rất nhiều. Tôi muốn chỉ cho bạn phần "Một số tính toán để điền vào phần []". Mà tôi nghĩ là cần thiết. Bạn tạo một [], sau đó bạn sao chép từ [] sang bộ đệm đầu ra (đó là những gì fux :: write thực hiện), sau đó bạn tạo lại, v.v.

Phải làm sao? Đa luồng! (Tôi hy vọng bạn có bộ xử lý đa lõi)

  • cái nĩa.
  • Sử dụng một luồng để tạo dữ liệu []
  • Sử dụng cái khác để ghi dữ liệu từ [] vào đĩa
  • Bạn sẽ cần hai mảng a1 [] và a2 [] và chuyển đổi giữa chúng
  • Bạn sẽ cần một số loại đồng bộ hóa giữa các chủ đề của bạn (semaphores, hàng đợi tin nhắn, v.v.)
  • Sử dụng các hàm cấp thấp hơn, không có bộ đệm, như hàm WriteFile được đề cập bởi Mehrdad

1

Nếu bạn muốn ghi nhanh vào các luồng tệp thì bạn có thể làm cho luồng bộ đệm đọc lớn hơn:

wfstream f;
const size_t nBufferSize = 16184;
wchar_t buffer[nBufferSize];
f.rdbuf()->pubsetbuf(buffer, nBufferSize);

Ngoài ra, khi ghi nhiều dữ liệu vào tệp, đôi khi nhanh hơn để mở rộng một cách hợp lý kích thước tệp thay vì vật lý, điều này là do khi mở rộng một cách hợp lý một tệp, hệ thống tệp không tạo khoảng trống mới trước khi ghi vào nó. Nó cũng là thông minh để mở rộng hợp lý tệp nhiều hơn bạn thực sự cần để ngăn chặn nhiều phạm vi tập tin. Mở rộng tệp logic được hỗ trợ trên Windows bằng cách gọi SetFileValidDatahoặc xfsctlvới XFS_IOC_RESVSP64các hệ thống XFS.


0

im biên soạn chương trình của tôi trong gcc trong GNU / Linuxmingw trong win 7 và win xp và tốt làm việc

bạn có thể sử dụng chương trình của tôi và để tạo tệp 80 GB, chỉ cần thay đổi dòng 33 thành

makeFile("Text.txt",1024,8192000);

Khi thoát khỏi chương trình, tập tin sẽ bị hủy, sau đó kiểm tra tập tin khi nó đang chạy

để có chương trình mà bạn muốn chỉ cần thay đổi chương trình

firt one là chương trình windows và cái thứ hai dành cho GNU / Linux

http://mustafajf.persiangig.com/Projects/File/WinFile.cpp

http://mustafajf.persiangig.com/Projects/File/File.cpp

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.