0

I prefer not to use XML library parser out there, so can you give me suggestion which good write function to use to write data to XML file? I will make alot of to calls to the write function so the write function should be able to keep track of the last write position and it should not take too much resource. I have two different write below but I can't keep track the last write position unless I have to read the file until end of file.

case#1

FILE *pfile = _tfopen(GetFileNameXML(), _T("w"));

if(pfile)
{
    _fputts(TEXT(""), pfile);
}

if(pfile)
{
    fclose(pfile);
    pfile = NULL;
}

case#2

HANDLE hFile = CreateFile(GetFileNameXML(), GENERIC_READ|GENERIC_WRITE,
    FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if(hFile != INVALID_HANDLE_VALUE)
{
    WriteFile(hFile,,,,,);
}

CloseHandle(hFile);

thanks.

Lufia
  • 135
  • 6
  • 14
  • 1
    Why my question being voted down? – Lufia Dec 24 '11 at 01:55
  • 2
    I am wondering the same thing, I don't see what is wrong with this question. C++ programmers should be encouraged unless it's a really extreme case. :) – Matt Phillips Dec 24 '11 at 01:58
  • It's probably being voted down because you mistakenly think that parsing small text files is an area where you can significantly improve performance by reinventing what has already been perfected. – Carey Gregory Dec 24 '11 at 06:57
  • 3
    Just wait until the bug reports start rolling in and then you'll realise that parsing and emitting XML is a non-trivial task and that using a well-tested, proven library is the right course of action. – David Heffernan Dec 24 '11 at 10:23

2 Answers2

2

If all you need is to write some text files, use C++'s standard library file facilities. The samples here will be helpful: http://www.cplusplus.com/doc/tutorial/files/

thesamet
  • 6,382
  • 2
  • 31
  • 42
-1

First, what's your aversion to using a standard XML processing library?

Next, if you decide to roll your own, definitely don't go directly at the Win32 APIs - at least not unless you're going to write out the generated XML in large chunks, or you're going to implement your own buffering layer.

It's not going to matter for dealing with tiny files, but you specifically mention good performance and many calls to the write function. WriteFile has a fair amount of overhead, it does a lot of work and involves user->kernel->user mode switches, which are expensive. If you're dealing with "normally sized" XML files you probably won't be able to see much of a difference, but if you're generating monstrously sized dumps it's definitely something to keep in mind.

You mention tracking the last write position - first off, it should be easy... with FILE buffers you have ftell, with raw Win32 API you have SetFilePointerEx - call it with liDistanceToMove=0 and dwMoveMethod=FILE_CURRENT, and you get the current file position after a write. But why do you need this? If you're streaming out an XML file, you should generally keep on streaming until you're done writing - are you closing and re-opening the file? Or are you writing a valid XML file which you want to insert more data into later?

As for the overhead of the Win32 file functions, it may or may not be relevant in your case (depending on the size of the files you're dealing with), but with larger files it matters a lot - included below is a micro-benchmark that simpy reads a file to memory with ReadFile, letting you specify different buffer sizes from the command line. It's interesting to look at, say, Process Explorer's IO tab while running the tool. Here's some statistics from my measly laptop (Win7-SP1 x64, core2duo P7350@2.0GHz, 4GB ram, 120GB Intel-320 SSD).

Take it for what it is, a micro-benchmark. The performance might or might not matter in your particular situation, but I do believe the numbers demonstrate that there's considerable overhead to the Win32 file APIs, and that doing a little buffering of your own helps.

With a fully cached 2GB file:

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

With a "so big there will only be cache misses" 4GB file:

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%

Keep in mind that 49% CPU usage means that one CPU core is pretty much fully pegged - a single thread can't really push the machine much harder. Notice the pathological behavior of the 4kb buffer in the second table - it was reproducible, and I don't have an explanation for it.

Crappy micro-benchmark code goes here:

#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;
}
snemarch
  • 4,958
  • 26
  • 38
  • 1
    Every single library out there that targets Windows and writes something out to a file will call `WriteFile()` at some point. Even the standard C and C++ file handling routines are implemented in terms of `WriteFile()` on Windows. So your criticism about using `WriteFile()` directly also applies to any XML processing library for Windows. – In silico Dec 24 '11 at 02:12
  • Thanks for the tip about WriteFile(). And what is the name of the standard XML processing library you're refering to? Because I've seen only Xerces, TinyXML and CMarkup. – Lufia Dec 24 '11 at 02:18
  • @Insilico "at some point", yes - hence my "very large chunks" comment. You'll want to do some in-application buffering (FILE*, iostream, your_own_scheme) instead of a lot of small WriteFile calls. – snemarch Dec 24 '11 at 02:22
  • 1
    @snemarch: I'm sure `WriteFile()` does in fact do some buffering for you, that's why there's a separate `FILE_FLAG_NO_BUFFERING` flag when you `CreateFile()` it in case you don't want buffering. And since I/O operations to disk, network, serial port, etc. tends to take a relatively long time, I would be surprised if the user- to kernel-mode transition made up a significant portion of the invocation time. – In silico Dec 24 '11 at 02:27
  • 1
    @Lufia I was just wondering why you wanted to whip up your own - aren't there any existing libraries that meet your needs? You could try looking at http://stackoverflow.com/questions/170686/best-open-xml-parser-for-c for inspiration. Of course if you only need to write, and have a simple fixed format, manually dumping the data can be a good solution. – snemarch Dec 24 '11 at 02:27
  • @Insilico yes, there's OS level filesystem caching - but there's a *lot* more code being executed if you go that way, compared to using in-app buffering. The overhead obviously depends on size of your writes, but it's generally noticeable. And keep in mind you're not necessarily the only process running on the system. – snemarch Dec 24 '11 at 02:30
  • @snemarch: I'm still not convinced that `WriteFile()` is significantly slower than using a language standard function for file writing to disk. My point was that even if you did in-app buffering yourself, you still need to call `WriteFile()` to actually write out the bytes. So you still need to run the code that implements `WriteFile()`. And [caching is done per file handle](http://msdn.microsoft.com/en-us/library/windows/desktop/aa364218(v=vs.85).aspx); there isn't a single file cache for all the running processes, so you're not competing for the cache. – In silico Dec 24 '11 at 02:41
  • @Insilico your caching link is a bit of a simplification - (page does say per file object, not handle, though). Check out the codesurgeon blog for more in-depth description. I've done a quick-and-dirty ReadFile benchmark (http://pastebin.com/XKFG39Ei) - yes, read != write, tested file is only ~600meg, and I'm measuring a fully cached file (factoring out disk bottleneck). Still, 406ms for 32kb blocksize vs 20592ms for 64byte blocksize says something about the overhead involved :) – snemarch Dec 24 '11 at 03:37
  • @snemarch: Your assertions are unfounded and your explanations make no sense. I'm extremely skeptical that adding a layer of buffering on top of a layer of buffering (actually, at least two layers) could possibly improve performance. But even if true, reading and writing XML? Who freaking cares about nanoseconds? It's a tiny text file. – Carey Gregory Dec 24 '11 at 06:53
  • @snemarch: Even if your assertion is correct (I haven't done a full analysis of your code snippet, and I don't have a ~600 MB file around to test with), how does it compare to standard C or C++ facilities like `FILE` or `fstream`? My first comment to your answer was that you mention `WriteFile()` should not be used, yet you suggest that a standard XML library should be used even though those libraries surely use `WriteFile()` at some point! Also, w.r.t the OP's question if you have ~600 MB XML files to read/write you have bigger problems. – In silico Dec 24 '11 at 07:54
  • 2
    In app buffering is needed with `WriteFile` if you are calling `WriteFile` with small sized blocks and performance matters. It doesn't take much experimentation to prove that. The overhead is that of calling an API function. – David Heffernan Dec 24 '11 at 10:26
  • 1
    @Insilico FILE and fstream both have their own buffering, for FILE it's default BUFSIZ bytes large but can be overridden by setvbuf. For fstream, you can override with stream.rdbuf()->pubsetbuf(). Yes, WriteFile will eventually be called, no discussion about that, my caution was because the OP posted a code snippet that directly called WriteFile, *presumably* going to be used without doing any buffering of his own. – snemarch Dec 24 '11 at 12:17
  • @CareyGregory make no sense? There's a lot of overhead in WriteFile, not least because it eventually does user->kernel->user mode switching, which is *expensive*. If there weren't any reason to do buffering, FILE* and fstream wouldn't do it. If you don't believe the overhead, try running the micro-benchmark tool I made (hint: 20sec vs 400ms runtime, depending on bufsize). And as for XML files always being tiny, well, I wish it was so :) – snemarch Dec 24 '11 at 12:23
  • @snemarch: I didn't say buffering isn't necessary; I said adding a second or third layer of buffering is unlikely to improve performance. The benefits of a good XML library far outweigh the flaws you imagine in WriteFile. – Carey Gregory Dec 24 '11 at 16:32
  • 2
    @CareyGregory so you agree that doing direct calls to WriteFile with very small write sizes is a bad idea? The OP states "I will make alot of to calls to the write function" and has a snippet involving WriteFile. If you're going to do a write call per XML entity, you'll probably find a lot of your writes are even smaller than 32 bytes. – snemarch Dec 24 '11 at 16:41
  • snemarch, thanks for the link above. I will take a look at RapidXml parser because I have to do this project both on embeddbed and windows. – Lufia Jan 03 '12 at 19:02