Hi , in my audio engine when I request a stream to be played off the harddisk, I just quickly read the file in chunks using ReadFile, but it is horribly slow as I can hear crackles and pops meaning that the ReadFile function is not responding as fast as it should to make this work. But I heard that I could map a very large file into memory without loading it explicitly and accessing like a regular buffer in RAM, can I use it for this purpose (streaming audio data of a hard disk or CD) and can you explain how I can make use of it (f0dder please save me, I believe it was you that suggested this before).
Posted on 2004-05-03 17:30:41 by x86asm
You can probably do this for audio yes... I don't know if it will be fast enough for chop-free audio though (a defragmented file also helps :)).
It works quite simple... You create a filemapping object... I believe CreateFile(), CreateFileMapping(), MapViewOfFile().
Then you get a pointer to the start of the file in memory.
What the OS does is quite simple. At the start, the file is not in memory yet, and all pages in the range of the memorymap are set up to cause an exception on access.
When you access such a page, the OS will catch the exception, read in that page from the file, make the page accessible, and continue your program.
So you can just randomly access the memory, and it will appear like the file is completely in memory, since the OS will transparently read in the pages on-demand (and it can also unload pages again, when they haven't been accessed in a while).
Posted on 2004-05-03 17:54:19 by Scali
You can open the file with CreateFile, then the map with CreateFileMapping. When you want to "read" stuff, make a call to MapViewOfFile with the section you want to read. If the whole file fits in RAM, just map it entirely (don't try this for large files under Win9X, the OS is just too dumb and will end up filling the RAM).

Readfile should work well though, it's probably easier to adapt yuor existing code to use asynchronous calls rather than file mappings, as I understand mappings won't give you much of a speed gain compared to Readfile (if used well). Particularly since you will be doing a sequential scan.
Posted on 2004-05-03 17:57:36 by QvasiModo
File mapping will be slower than ReadFile calls, as ReadFile doesn't incur the Page Fault exception overheads. Ok, you don't get a #PF for each 4k page, more like 64kb - but obviously no pagefaults are better than "just a few". Memory-mapped files can make things easy, but it's slower than regular file access.

The trick is to preload your data, or have a background worker thread with a modified priority, and a good signalling system. Ie, don't read from file when the playback buffer is empty, but when it's 2/3rd done or something like that.

Async file I/O could probably also be blended in for good results.

Also, have a look here:
http://www.flipcode.com/cgi-bin/msg.cgi?showThread=Tip-ReducingGameLoadTimes&forum=totd&id=-1
Posted on 2004-05-03 18:20:27 by f0dder
You can get a litle help from DirectX (DirectSound) seccondary play buffers:
they have an option to notify you when a certain play position was reached in the buffer/stream.
(btw leave the primary buffer playing at all times for low latency)

So, on that notify procedure you can just read another chunk of audio data inside your buffer.

Keep this notify position 2-3s ahead of buffer end and you should be safe most of the time. Of course occasional stalls might still occur, like when a huge App is starting (3Dstudio Max :D) or when YM will pop-up a message window :grin:. Everything can be solved with a bigger buffer ;)


There is nothing faster than a well written ReadFile algorithm. And it is logical since a big part of on OS performance is based on fast reading and writtiong files ... this is a basic and important function of an OS.
Posted on 2004-05-04 02:29:14 by BogdanOntanu
Originally posted by f0dder
File mapping will be slower than ReadFile calls, as ReadFile doesn't incur the Page Fault exception overheads. Ok, you don't get a #PF for each 4k page, more like 64kb - but obviously no pagefaults are better than "just a few". Memory-mapped files can make things easy, but it's slower than regular file access.



IIRC, you've made this claim before, and it was just as incorrect then as it is now.
Try writing two programs that step sequentially through a large file, one that reads the data 4KB at a time and one that walks a pointer through a memory mapped file. You'll find that the performance is virtually identical.

As for the page fault issue, what on earth makes you think that copying file data across buffers between kernel and user space is going to be a whole lot better?

Memory mapped files, however, can take a big lead when you're working on small chunks of the file (particularly r/w, random access, though this doesn't particularly apply to the OP's problem, I suspect).

Bottom line is that you've seen this argument before, you've seen sample programs that demonstrate that what you're claiming is not true. So why do you continue to insist that memory mapped files are so much slower? Again, you don't have to believe me -- try it yourself.
Cheers,
Randy Hyde
Posted on 2004-05-04 15:17:10 by rhyde

Try writing two programs that step sequentially through a large file, one that reads the data 4KB at a time and one that walks a pointer through a memory mapped file. You'll find that the performance is virtually identical.

Why 4Kb at a time? Memory mapped files implementation on win32 reads 64kb at a time..


As for the page fault issue, what on earth makes you think that copying file data across buffers between kernel and user space is going to be a whole lot better?

The file data isn't being copied anyway...


Memory mapped files, however, can take a big lead when you're working on small chunks of the file (particularly r/w, random access, though this doesn't particularly apply to the OP's problem, I suspect).

No, the advantages for memory mapped files are IPC, large virtual memory allocations and convenience at times.
Posted on 2004-05-04 15:32:19 by death
I would recommend an async reading strategy anyway, into a cyclic buffer with 2 or perhaps 3 sections.
Posted on 2004-05-04 15:52:06 by Scali

one that reads the data 4KB at a time and one that walks a pointer through a memory mapped file.

Why would I want to do ReadFile with 4kb buffers? The advantage regular ReadFile gives you is, among other things, that you can control the buffer size. By the use of a larger buffer size, you get fewer of the costy ring3<>ring0 transitions. Besides, thankfully, the #PF's aren't done per 4kb page when dealing with MMF's - so the buffer should be at least 64kb. I wouldn't use such a small buffer if dealing with 'massive' files though, but probably rather between 256-1024kb.

You can't really do async I/O with MMF either...


Memory mapped files, however, can take a big lead when you're working on small chunks of the file (particularly r/w, random access, though this doesn't particularly apply to the OP's problem, I suspect).

If your current algorithm is "read byte, modify byte, write byte" - then, yes, the memory mapped approach will be better. If that's your data usage pattern and you can't use a smarter algorithm, then memory mapped files might turn out better.

However, the context of this thread is streaming in audio data. This means you need all the bytes, you need them linearly, and you need rather massive amounts (well, if streaming a regular .wav anyway). The only advantage MMF brings here is convenience - and you put yourself at the mercy of the windows caching and read-ahead strategy rather than keeping full control.

Btw, when dealing with writable MMF, you fortunately don't get a #PF for every write access. The page is written as dirty, and the "lazy page writer" thread then, according to internal windows strategies, determine when to write the page to disk and mark the page clean. This means that you don't really know when data will be flushed, and that you (with a very scattered access pattern) could be causing a lot of #PF's. Would take "a bunch of logic" to improve on this with regular file access, and the overhead might not be worth it. Then again, the topic in this thread is not random data access but linear streaming of audio data.


As for the page fault issue, what on earth makes you think that copying file data across buffers between kernel and user space is going to be a whole lot better?

If this buffer copying takes place, rather than reading directly to the user-supplied buffer, I would very much suspect that a simple memory copy routine takes a lot less time than the overhead of a pagefault, the checks needed to figure out why it happened (should we terminate the program, is it a valid pagefault (ie, MMF), et cetera.) Yes, a lot of this overhead will drown in the disk I/O overhead...

Anyway, I would recommend async I/O if you don't need 9x support, or a background worker thread with adjusted priority, signalled when the audio buffer is "running low", doing regular ReadFile I/O.
Posted on 2004-05-04 16:03:44 by f0dder