3

When instantiating a class with new. Instead of deleting the memory what kinds of benefits would we gain based on the reuse of the objects?

What is the process of new? Does a context switch occur? New memory is allocated, who is doing the allocation? OS ?

mpen
  • 272,448
  • 266
  • 850
  • 1,236
RoR
  • 15,934
  • 22
  • 71
  • 92
  • 7
    Using another allocator other than the standard one, should be based on profiling results or by design(e.g. memory pool). Other than that, the allocators that comes with the implementations are quit good for most cases. – Khaled Alshaya Jan 06 '11 at 08:37
  • Good question indeed. I have had one case where, after profiling, I switched to a pool allocator for many small and constant sized objects. I think that a context switch only occurs for allocating several pages at once (with mmap on linux), and these pages are reused internally by the allocator. Now, there are different allocation strategies if you know beforehand that the objects you allocate will all have the same size. Standard library allocators are tuned for the general case, not specific ones. I heard that some allocators maintain several memory pools for small, medium and large objects. – Alexandre C. Jan 06 '11 at 09:33

3 Answers3

5

You've asked a few questions here...

Instead of deleting the memory what kinds of benefits would we gain based on the reuse of the objects?

That depends entirely on your application. Even supposing I knew what the application is, you've left another detail unspecified -- what is the strategy behind your re-use? But even knowing that, it's very hard to predict or answer generically. Try some things and measure them.

As a rule of thumb I like to minimize the most gratuitous of allocations. This is mostly premature optimization, though. It'd only make a difference over thousands of calls.

What is the process of new?

Entirely implementation dependent. But the general strategy that allocators use is to have a free list, that is, a list of blocks which have been freed in the process. When the free list is empty or contains insufficient contiguous free space, it must ask the kernel for the memory, which it can only give out in blocks of a constant page size. (4096 on x86.) An allocator also has to decide when to chop up, pad, or coalesce blocks. Multi-threading can also put pressure on allocators because they must synchronize their free lists.

Generally it's a pretty expensive operation. Maybe not so much relative to what else you're doing. But it ain't cheap.

Does a context switch occur?
Entirely possible. It's also possible that it won't. Your OS is free to do a context switch any time it gets an interrupt or a syscall, so uh... That can happen at a lot of times; I don't see any special relationship between this and your allocator.

New memory is allocated, who is doing the allocation? OS ?
It might come from a free list, in which case there is no system call involved, hence no help from the OS. But it might come from the OS if the free list can't satisfy the request. Also, even if it comes from the free list, your kernel might have paged out that data, so you could get a page fault on access and the kernel's allocator would kick in. So I guess it'd be a mixed bag. Of course, you can have a conforming implementation that does all kinds of crazy things.
asveikau
  • 39,039
  • 2
  • 53
  • 68
  • There is a kind of special relationship with an allocator, the kernel, and locks. If there is not enough heap memory obviously a syscall is involved, which will do a context switch. If access to the heap must be serialized then a lock is involved, which may again require a syscall and context switch. For high performance apps this possible syscall is important -- even if in most cases it won't do it. – edA-qa mort-ora-y Jan 06 '11 at 11:00
2
  • new allocates memory for the class on the heap, and calls the constructor.
  • context switches do not have to occur.
  • The c++-runtime allocates the memory on its freestore using whatever mechanism it deems fit.

Usually the c++ runtime allocates large blocks of memory using OS memory management functions, and then subdivides those up using its own heap implementation. The microsoft c++ runtime mostly uses the Win32 heap functions which are implemented in usermode, and divide up OS memory allocated using the virtual memory apis. There are thus no context switches until and unless its current allocation of virtual memory is needed and it needs to go to the OS to allocate more.

There is a theoretical problem when allocating memory that there is no upper bound on how long a heap traversal might take to find a free block. Practically tho, heap allocations are usually fast.

With the exception of threaded applications. Because most c++ runtimes share a single heap between multiple threads, access to the heap needs to be serialized. This can severly degrade the performance of certain classes of applications that rely on multiple threads being able to new and delete many objects.

Chris Becke
  • 34,244
  • 12
  • 79
  • 148
1

If you new or delete an address it's marked as occupied or unassigned. The implementations do not talk all the time with the kernel. Bigger chucks of memory are reserved and divided in smaller chucks in user space within your application.

Because new and delete are re-entrant (or thread-safe depending on the implementation) a context switch may occur but your implementation is thread-safe anyway while using the default new and delete.

In C++ you are able to overwrite the new and delete operator, e.g. to place your memory management:

#include <cstdlib> //declarations of malloc and free
#include <new>
#include <iostream>
using namespace std;

class C {
public:
  C(); 
  void* operator new (size_t size); //implicitly declared as a static member function
  void operator delete (void *p); //implicitly declared as a static member function
};

void* C::operator new (size_t  size) throw (const char *){
  void * p = malloc(size);
  if (p == 0)  throw "allocation failure";  //instead of std::bad_alloc
  return p; 
}

void C::operator delete (void *p){  
  C* pc = static_cast<C*>(p); 
  free(p);  
}

int main() { 
   C *p = new C; // calls C::new
   delete p;  // calls C::delete
}
Raphael Bossek
  • 1,904
  • 14
  • 25