Во-первых, каково ваше отвращение к использованию стандартной библиотеки обработки XML?
Далее, если вы решите свернуть свою собственную, определенно не обращайтесь напрямую к Win32 API - по крайней мере, если вы несобираемся записывать сгенерированный XML большими кусками, или вы собираетесь реализовать свой собственный уровень буферизации.
Это не имеет значения для работы с крошечными файлами, но вы особо упомянули хорошую производительность и много обращений кфункция записи.WriteFile требует значительных накладных расходов, выполняет много работы и включает переключатели режима user-> kernel-> user, которые стоят дорого.Если вы имеете дело с «обычными по размеру» XML-файлами, вы, вероятно, не сможете увидеть большую разницу, но если вы создаете дампы чудовищного размера, это, безусловно, кое-что, что нужно иметь в виду.
Выупомяните отслеживание последней позиции записи - во-первых, это должно быть легко ... с буферами FILE у вас есть ftell , с необработанным Win32 API у вас есть SetFilePointerEx - вызовите его с помощью liDistanceToMove=0
и dwMoveMethod=FILE_CURRENT
, и вы получите текущую позицию файла после записи.Но зачем тебе это?Если вы транслируете XML-файл, вам обычно следует продолжать потоковую передачу до тех пор, пока вы не закончите запись - закрываете ли вы и снова открываете файл?Или вы пишете правильный XML-файл, в который вы хотите вставить больше данных позже?
Что касается накладных расходов на файловые функции Win32, это может или не может иметь значение в вашем случае (в зависимости от размерафайлы, с которыми вы имеете дело), но для больших файлов это имеет большое значение - ниже приведен микро-эталон, который упрощает чтение файла в память с помощью ReadFile, позволяя вам указать разные размеры буфера из командной строки.Интересно, например, посмотреть на вкладку ввода-вывода Process Explorer во время работы инструмента.Вот некоторая статистика с моего жалкого ноутбука (Win7-SP1 x64, core2duo P7350 @ 2.0 ГГц, 4 ГБ оперативной памяти, 120 ГБ твердотельный накопитель Intel-320).
Примите это за микропроцессор.Производительность может или не может иметь значения в вашей конкретной ситуации, но я полагаю, что цифры демонстрируют, что существуют значительные издержки для файловых API-интерфейсов Win32, и что небольшая буферизация помогает вам.
С полностью кэшированнымФайл 2 ГБ:
BlkSz Speed
32 14.4MB/s
64 28.6MB/s
128 56MB/s
256 107MB/s
512 205MB/s
1024 350MB/s
4096 800MB/s
32768 ~2GB/s
При значении «настолько велики будут только кешированные пропуски» файл 4 ГБ:
BlkSz Speed CPU
32 13MB/s 49%
64 26MB/s 49%
128 52MB/s 49%
256 99MB/s 49%
512 180MB/s 49%
1024 200MB/s 32%
4096 185MB/s 22%
32768 205MB/s 13%
Имейте в виду, что 49% использования ЦП означает, что одно ядро ЦПв значительной степени полностью прикреплен - одна нить не может толкнуть машину намного сильнее.Обратите внимание на патологическое поведение буфера 4 КБ во второй таблице - он был воспроизводим, и у меня нет объяснения этому.
Слабый микропроцессорный код здесь:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <string>
#include <assert.h>
unsigned getDuration(FILETIME& timeStart, FILETIME& timeEnd)
{
// duration is in 100-nanoseconds, we want milliseconds
// 1 millisecond = 1000 microseconds = 1000000 nanoseconds
LARGE_INTEGER ts, te, res;
ts.HighPart = timeStart.dwHighDateTime; ts.LowPart = timeStart.dwLowDateTime;
te.HighPart = timeEnd.dwHighDateTime; te.LowPart = timeEnd.dwLowDateTime;
res.QuadPart = ((te.QuadPart - ts.QuadPart) / 10000);
assert(res.QuadPart < UINT_MAX);
return res.QuadPart;
}
int main(int argc, char* argv[])
{
if(argc < 2) {
puts("Syntax: ReadFile [filename] [blocksize]");
return 0;
}
char *filename= argv[1];
int blockSize = atoi(argv[2]);
if(blockSize < 1) {
puts("Please specify a blocksize larger than 0");
return 1;
}
HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
if(INVALID_HANDLE_VALUE == hFile) {
puts("error opening input file");
return 1;
}
std::vector<char> buffer(blockSize);
LARGE_INTEGER fileSize;
if(!GetFileSizeEx(hFile, &fileSize)) {
puts("Failed getting file size.");
return 1;
}
std::cout << "File size " << fileSize.QuadPart << ", that's " << (fileSize.QuadPart / blockSize) <<
" blocks of " << blockSize << " bytes - reading..." << std::endl;
FILETIME dummy, kernelStart, userStart;
GetProcessTimes(GetCurrentProcess(), &dummy, &dummy, &kernelStart, &userStart);
DWORD ticks = GetTickCount();
DWORD bytesRead = 0;
do {
if(!ReadFile(hFile, &buffer[0], blockSize, &bytesRead, 0)) {
puts("Error calling ReadFile");
return 1;
}
} while(bytesRead == blockSize);
ticks = GetTickCount() - ticks;
FILETIME kernelEnd, userEnd;
GetProcessTimes(GetCurrentProcess(), &dummy, &dummy, &kernelEnd, &userEnd);
CloseHandle(hFile);
std::cout << "Reading with " << blockSize << " sized blocks took " << ticks << "ms, spending " <<
getDuration(kernelStart, kernelEnd) << "ms in kernel and " <<
getDuration(userStart, userEnd) << "ms in user mode. Hit enter to countinue." << std::endl;
std::string dummyString;
std::cin >> dummyString;
return 0;
}