6

I am writing an app which receives a binary data stream wtih a simple function call like put(DataBLock, dateTime); where each data package is 4 MB

I have to write these datablocks to seperate files for future use with some additional data like id, insertion time, tag etc...

So I both tried these two methods:

first with FILE:

data.id = seedFileId; 
seedFileId++;

std::string fileName = getFileName(data.id);

char *fNameArray = (char*)fileName.c_str(); 
FILE* pFile;
pFile = fopen(fNameArray,"wb");

fwrite(reinterpret_cast<const char *>(&data.dataTime), 1, sizeof(data.dataTime), pFile);        
data.dataInsertionTime = time(0);
fwrite(reinterpret_cast<const char *>(&data.dataInsertionTime), 1, sizeof(data.dataInsertionTime), pFile);
fwrite(reinterpret_cast<const char *>(&data.id), 1, sizeof(long), pFile);
fwrite(reinterpret_cast<const char *>(&data.tag), 1, sizeof(data.tag), pFile);
fwrite(reinterpret_cast<const char *>(&data.data_block[0]), 1, data.data_block.size() * sizeof(int), pFile);
fclose(pFile);

second with ostream:

ofstream fout;
data.id = seedFileId; 
seedFileId++;

std::string fileName = getFileName(data.id);
char *fNameArray = (char*)fileName.c_str(); 
fout.open(fNameArray, ios::out| ios::binary | ios::app);


fout.write(reinterpret_cast<const char *>(&data.dataTime), sizeof(data.dataTime));      
data.dataInsertionTime = time(0);
fout.write(reinterpret_cast<const char *>(&data.dataInsertionTime), sizeof(data.dataInsertionTime));
fout.write(reinterpret_cast<const char *>(&data.id), sizeof(long));
fout.write(reinterpret_cast<const char *>(&data.tag), sizeof(data.tag));
fout.write(reinterpret_cast<const char *>(&data.data_block[0]), data.data_block.size() * sizeof(int));
fout.close();

In my tests the first methods looks faster, but my main problem is in both ways at first everythings goes fine, for every file writing operation it tooks almost the same time (like 20 milliseconds), but after the 250 - 300th package it starts to make some peaks like 150 to 300 milliseconds and then goes down to 20 milliseconds and then again 150 ms and so on... So it becomes very unpredictable.

When I put some timers to the code I figured out that the main reason for these peaks are because of the fout.open(...) and pfile = fopen(...) lines. I have no idea if this is because of the operating system, hard drive, any kind of cache or buffer mechanism etc...

So the question is; why these file opening lines become problematic after some time, and is there a way to make file writing operation stable, I mean fixed time?

Thanks.

NOTE: I'm using Visual studio 2008 vc++, Windows 7 x64. (I tried also for 32 bit configuration but the result is same)

EDIT: After some point writing speed slows down as well even if the opening file time is minimum. I tried with different package sizes so here are the results:

For 2 MB packages it takes double time to slow down, I mean after ~ 600th item slowing down begins

For 4 MB packages almost 300th item

For 8 MB packages almost 150th item

So it seems to me it is some sort of caching problem or something? (in hard drive or OS). But I also tried with disabling hard drive cache but nothing changed...

Any idea?

pnuts
  • 58,317
  • 11
  • 87
  • 139
user2955554
  • 309
  • 3
  • 14
  • 2
    `char *fNameArray = (char*)fileName.c_str();` => NO! Use `char const* fNameArray = fileName.c_str();` instead. (it's `const` for a reason) Other than that, your issue is probably more related to OS/hardware issues than the language/compiler. – Matthieu M. Mar 31 '14 at 11:12
  • I tried it with different OS/hardware configurations the result is almost the same. So can it be a bug in Windows OS? – user2955554 Mar 31 '14 at 11:36
  • Do you create all files in the same directory ? There might be leaps of performance when the directory has to allocate new space for its list of files (imagine, for example, if it stores files in blocks of 300 entries). – Matthieu M. Mar 31 '14 at 11:54
  • yes all the files are in the same directory. Actually I can calculate the total size of this directory from beginning, is there a way to reserve/allocate space for the directory to prevent that leap? – user2955554 Mar 31 '14 at 11:58
  • I am afraid this is beyond my knowledge (and is probably depending on the particular filesystem you are using), another solution would be to keep the number of files below 250/300 per directory if this is the point it starts behaving badly. – Matthieu M. Mar 31 '14 at 13:49
  • Now I just tried to write each 100 item into a different directory. But nothing changed unfortunately. Can it be related with hard drive's cache or something? Thanks for answers by the way. – user2955554 Mar 31 '14 at 14:50
  • Might be :x I am unfortunately under-experimented here :x – Matthieu M. Mar 31 '14 at 14:59
  • :) ok thanks anyway. By the way I just turned off the hard disk caching, but not a big change... – user2955554 Mar 31 '14 at 15:05
  • To check if it's really hard drive I/O problem make a [RAM disk](http://memory.dataram.com/products-and-services/software/ramdisk) and test on it, this will eliminate I/O influence to a large extend. – MojaveWastelander Apr 02 '14 at 12:20
  • 1
    This doesn't sound like a bug. Windows file caching is a vast subject. I suggest this pretty old but still excellent article http://technet.microsoft.com/en-us/library/bb742613.aspx . You should first concentrate on Performance Counters to analyze what's going on, and possibly fix things then. – Simon Mourier Apr 02 '14 at 16:13
  • One (totally not cross platform) way is to pre-allocate the files. http://stackoverflow.com/questions/7970333/how-do-you-pre-allocate-space-for-a-file-in-c-c-on-windows. You'll still get un-even performance, but it'll be a little better. – IdeaHat Apr 04 '14 at 18:36
  • I'd like to suggest you to replace `time(0)` with `GetProcessTimes`, thus you can check the time consumed by the process (including user space and kernel space), instead of the whole time delayed. – Peixu Zhu Apr 06 '14 at 10:52

3 Answers3

9

This is all perfectly normal, you are observing the behavior of the file system cache. Which is a chunk of RAM that's is set aside by the operating system to buffer disk data. It is normally a fat gigabyte, can be much more if your machine has lots of RAM. Sounds like you've got 4 GB installed, not that much for a 64-bit operating system. Depends however on the RAM needs of other processes that run on the machine.

Your calls to fwrite() or ofstream::write() write to a small buffer created by the CRT, it in turns make operating system calls to flush full buffers. The OS writes normally completely very quickly, it is a simple memory-to-memory copy going from the CRT buffer to the file system cache. Effective write speed is in excess of a gigabyte/second.

The file system driver lazily writes the file system cache data to the disk. Optimized to minimize the seek time on the write head, by far the most expensive operation on the disk drive. Effective write speed is determined by the rotational speed of the disk platter and the time needed to position the write head. Typical is around 30 megabytes/second for consumer-level drives, give or take a factor of 2.

Perhaps you see the fire-hose problem here. You are writing to the file cache a lot faster than it can be emptied. This does hit the wall eventually, you'll manage to fill the cache to capacity and suddenly see the perf of your program fall off a cliff. Your program must now wait until space opens up in the cache so the write can complete, effective write speed is now throttled by disk write speeds.

The 20 msec delays you observe are normal as well. That's typically how long it takes to open a file. That is a time that's completely dominated by disk head seek times, it needs to travel to the file system index to write the directory entry. Nominal times are between 20 and 50 msec, you are on the low end of that already.

Clearly there is very little you can do in your code to improve this. What CRT functions you use certainly don't make any difference, as you found out. At best you could increase the size of the files you write, that reduces the overhead spent on creating the file.

Buying more RAM is always a good idea. But it of course merely delays the moment where the firehose overflows the bucket. You need better drive hardware to get ahead. An SSD is pretty nice, so is a striped raid array. Best thing to do is to simply not wait for your program to complete :)

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks for the detailed answer. After the point that i mentioned not only the file opening line but also the writing to file process becomes inaccurate as well. So even if i write all the data to one file the result is same. Is there a way to clear system cache manually after every write process? I need consistent way first. For ex. 20 msec at beginning, 70 to 150 msec after some point is useless for me. For instance 40 msec consistent every time would make more sense for my situation. Is there a way to achieve this? – user2955554 Apr 06 '14 at 20:28
  • That's a pretty disappointing comment after I made the effort to explain how the machine worked. If you understood the answer then you already know what to do to make it consistent. Call Sleep(10000) after writing a file so the disk can catch up with the cache. You'll now *very* consistently write one file every 10 seconds. – Hans Passant Apr 07 '14 at 00:04
  • sorry for the dissapointment :) but I hoped a conclusion or let's say an exact solution but I guess there is none in this situtaion. I totally understand what you saying but the problem is I have a stream like 20 packages per second so it is impossible for me to put 10 seconds sleep times. Before asking the quest. I also tried to put some small delays like 20msec but it didn't make much difference. I wondered if there is a backdoor or something. In the real system it will be an ssd but I don't like to depend strictly on hardware like more ram and better hard drive etc.. Anyway thanks again. – user2955554 Apr 07 '14 at 11:51
  • SSDs might be nice, but a RAID-0 array might be better since the problem here is being caused by a mismatch between read-from-source and write-to-disk. If you had sufficient disks in the array, you might be able - it would depend on the actual speeds in the system, obviously - to bring those speeds closer together. And if the read-from-source speed isn't constant but varys, you might be able to get close enough to eliminate the problem in practice. – Mark Dennehy Apr 08 '14 at 12:50
  • But if you need absolutely consistent times, the only real solution you're going to have if you can't muck about with hardware is to put a brake on the read-from-source speed, even if you do it artificially by inserting a post-read delay and flush() call so that you get the data to disk (or at least enough of it so that you can stay caught up with the source). But something like this smells like the kind of problem that's just going to require you to muck about with hardware, at least with this design. – Mark Dennehy Apr 08 '14 at 12:53
  • Yeah we can try some hardware dependent solutions, it will be better with ssd for sure. But soon or later, insome point there will be a bottle neck again I think. So when I think that the system will run for ours with a constant frequency again it will be lack of hardware resources. That's why I am looking for a software solution. By the way what do you mean by "at least with design"? Do you have any suggestion to keep tha data in hard drive? Thanks... – user2955554 Apr 09 '14 at 11:12
1

So the question is; why these file opening lines become problematic after some time, and is there a way to make file writing operation stable, I mean fixed time?

This observation(.i.e. varying time taken in write operation) does not mean that there is problem in OS or File System.There could be various reason behind your observation. One possible reason could be the delayed write may be used by kernel to write the data to disk. Sometime kernel cache it(buffer) in case another process should read or write it soon so that extra disk operation can be avoided.

This situation may lead to inconsistency in the time taken in different write call for same size of data/buffer.

File I/O is bit complex and complicated topic and depends on various other factors. For complete information on internal algorithm on File System, you may want to refer the great great classic book "The Design Of UNIX Operating System" By Maurice J Bach which describes these concepts and the implementation in detailed way.

Having said that, you may want to use the flush call immediately after your write call in both version of your program(.i.e. C and C++). This way you may get the consistent time in your file I/O write time. Otherwise your programs behaviour look correct to me.

//C program
fwrite(data,fp);
fflush(fp);

//C++ Program
fout.write(data);
fout.flush();
Mantosh Kumar
  • 5,659
  • 3
  • 24
  • 48
  • I tried both of them previously. Unfortunately nothing changed... Thanks – user2955554 Apr 06 '14 at 20:20
  • @user2955554: Well I have nothing to add what I have posted above. I feel that your observation is kind of limitation of file system and there is nothing much which we can do/improve it.However I would suggest you to read the great book mentioned in my post. You will understand why there is such limitation. – Mantosh Kumar Apr 07 '14 at 02:49
0

It's possible that the spikes are not related to I/O itself, but NTFS metadata: when your file count reach some limit, some NTFS AVL-like data structure needs some refactoring and... bump!

To check it you should preallocate the file entries, for example creating all the files with zero size, and then opening them when writing, just for testing: if my theory is correct you shouldn't see your spikes anymore.

UHH - and you must disable file indexing (Windows search service) there! Just remembered of it... see here.

Community
  • 1
  • 1
Sigi
  • 4,826
  • 1
  • 19
  • 23
  • Actually the fact that the time-unreliable operation is the "open", seems to indicate that it's really a "problem" related to the filesystem metadata. Another test that you can perform in order to absolve caching, is to write all the files in append to just one file. – Sigi Apr 09 '14 at 11:15
  • Yeah I already tried it. I wrote all the data into one file but this time slowness starts at the same point (almost 300th item). I mean even if the "open" line is ok (I mean 1 msec) the writing data is becoming problematic. – user2955554 Apr 09 '14 at 12:17