4

I noticed this weird effect where memory is not registered as allocated by the Windows Task Manager until it is touched instead of when it is malloc-ed or new-ed. The effect occurs in both the debug and optimized release builds.

The following is a structural example, although in my code the allocation and utilization occurs on different threads so I don't think its the optimizer, although I am not sure how to check.

for (int i = 0 ;i < 1000;i++)
{
buffer[i]=malloc(buffersize);
}
_sleep(1000*60)
for (int i=0;i<1000;i++)
{
memset(buffer[i],0,buffersize);//Only shows up the in the resource manager here
}

My question is how does Windows know I have used the memory? Is it monitoring memory for first usage or is it some compile time optimization.

My curiosity is motivated by a realtime acquisition I am writing that requires me to touch the memory twice -> once when allocating and once when actually filling it with data. Thus pressing a button ("aquire!") requires me to write 64 gigabytes of ram at once, as opposed to over time, adding a fairly real amount of latency. If I malloc as I go this adds too much latency.

--edit--

I also disabled the Windows pagefile...

Mikhail
  • 7,749
  • 11
  • 62
  • 136

4 Answers4

7

This is standard behavior for a demand-page virtual memory operating system like Windows. The malloc() call only allocates virtual memory address space. You don't actually start using RAM until you access the memory. Which generates a page-fault, forcing the operating system to map the memory page you accessed into RAM.

This is normally a soft page fault, handled very quickly by grabbing a RAM page off the free list and mapping it. As opposed to a hard page fault, one you'll suffer later when some of those RAM pages got swapped out again to the swap file because another process needed RAM. Reloading the page from disk takes more time.

Disabling the paging file helps avoid those hard page faults. It doesn't eliminate them, your code pages are subject to getting swapped out as well. Which may well happen when you force the OS to fall back to them since it can't swap out to the paging file anymore. Such a page is just discarded when swapped out, reloaded from the executable file when page-faulted back in.

If you have soft realtime requirements then the best strategy is to allocate memory early and intentionally access it before you start to promise quick responses. Could simply be done with calloc() instead of malloc().

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
3

Virtual address space allocated to your process is not necessarily backed by physical RAM. One example is the paging file, where memory that's in use may be moved to if physical RAM is running low. But there doesn't have to be any physical RAM or disk space allocated at all if you've never even written to it. Hence, as an optimization, some operating systems give you virtual address space without actually allocating space for it yet. In some cases, this is very beneficial. In other cases, like yours, it's a bit annoying.

That said, the task manager is a pretty simplistic tool for investigating memory usage. It's more of a lucky coincidence you were able to observe this, many problems can't (reliably) be observed using only the task manager.

As for how to avoid lazy allocation without touching all memory... I don't know of any way. Not even VirtualAlloc offers a way around this. You can cut down the costs a bit by only writing one byte per page, that should still cause physical RAM allocation. But keep in mind, even if you could avoid writing anything, Windows would still have to allocate 16 million pages, including page table entries. That's bound to take some time. You can use large pages (using VirtualAlloc), which makes pages a few hundred times larger and hence reduces the aforementioned 16 million pages to a more reasonable figure.

1

It seems like your OS is allocating memory lazily. See the answers to this question

Basically, when you call malloc, the OS gives your program an address, and a promise to make memory available in the amount requested. The analogy I've read is that the OS "writes a check" for the memory, but only allocates physical memory when your program attempts to "cash" the check by using it.

I think if you want the latency hit to come up front when you malloc, you should memset there.

Community
  • 1
  • 1
NicholasM
  • 4,557
  • 1
  • 20
  • 47
  • Which is what I am doing, but it sucks because I have to memset a large amount of memory, at once which results in a noticeable latency. – Mikhail Nov 03 '13 at 16:55
  • I am suggesting to do this: `for (..) { buffer[i]=malloc(buffersize); memset(buffer,0,buffersize); }` – NicholasM Nov 03 '13 at 16:56
1

You can try both malloc and immediately assign an empty value to it to overcome the 'lazy memory allocation' NicholasM was talking about. Even then, the compiler may figure out that you are assigning but not using the assignments and remove them from the compiled code. But don't forget why the OS allocates memory lazily.

aemreunal
  • 85
  • 1
  • 1
  • 7