Ответ 1
Это определенно проблема с fwrite
(я пробовал как VS2012, так и 2010).
Начиная со стандартного проекта на С++, я изменил только параметр, чтобы использовать многобайтовый набор символов, цель x64 и многопоточную версию отладки стандартной библиотеки в статической ссылке.
Следующий код преуспевает (без ошибок для краткости):
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
long long n;
unsigned char *data;
n = 4LL * 1024 * 1024 * 1024 - 1;
data = (unsigned char *)malloc(n * sizeof(unsigned char));
fp = fopen("T:\\test.bin", "wb");
fwrite(data, sizeof(unsigned char), n, fp);
fclose(fp);
}
В отладочной версии моей машины программа заканчивается примерно через 1 минуту (malloc занимает всего несколько секунд, поэтому это в основном fwrite
), потребляя в среднем 30% CPU. PerfMon показывает, что запись происходит целиком в конце - это одна "вспышка" 4 ГБ (кэш записи).
Измените - 1
на a + 1
в назначении n, и вы воспроизведете проблему: мгновенное 100% использование ЦП и ничего не записывается. Через несколько минут размер файла оставался 0 байтами (напомним, что в моем фактическом коде ему удается сбросить 70 МБ или около того).
Это определенно проблема в fwrite
, так как следующий код может просто написать файл:
int main()
{
FILE *fp;
long long n;
long long counter = 0;
long long chunk;
unsigned char *data;
n = 4LL * 1024 * 1024 * 1024 + 1;
data = (unsigned char *)malloc(n * sizeof(unsigned char));
fp = fopen("T:\\test.bin", "wb");
while (counter < n)
{
chunk = min(n - counter, 100*1000);
fwrite(data+counter, sizeof(unsigned char), chunk, fp);
counter += chunk;
}
fclose(fp);
}
На моей машине это заняло 45 секунд вместо 1 минуты. Использование ЦП не является постоянным, оно поступает в пакеты, а сообщения IO более распределены, чем в методе "один кусок".
Я был бы очень удивлен, если бы увеличение скорости было ложным (то есть из-за кэширования), потому что я делал тесты перед написанием нескольких файлов, содержащих все те же данные, что и файлы, содержащие рандомизированные данные, и сообщенную скорость записи (с кеширование) одинаковы. Поэтому я готов поспорить, что по крайней мере эта реализация fwrite
не нравится, когда в нее передаются огромные куски.
Я также тестировал fread
для чтения сразу после закрытия файла для записи в случае 4 GB + 1, и он возвращается своевременно - всего несколько секунд (никаких реальных данных здесь, поэтому я не проверял его).
ИЗМЕНИТЬ
Я провел несколько тестов с помощью метода кусочной записи и одного вызова fwrite из файла размером 4 ГБ-1 (наибольший размер, который могут выполнять оба метода). Запуск программы несколько раз (с кодом, таким образом, чтобы файл был открыт, написан с несколькими вызовами fwrite, закрыт, затем снова открыт, написан в одном вызове и закрыт), нет сомнений в том, что метод записи блоков быстрее. В худшем случае он возвращается в 68% случаев, когда требуется один звонок, и в лучшем случае я получил всего 20%.