If the pages don’t already exist (as indicated by the given timings), this is a test of the OS and has little to do with the language.
It’s a poorly posed question. C++ runs on many environments.
If you are allocating lots of memory and don't know about hugepages, you badly need to learn.
If your program has threads, unmapping memory is generally to be avoided.
Could you expand on this? I recently encountered a situation where allocating and freeing many large allocations using mmap() seemed to eventually cause problems with thread creation, but I had assumed that it was probably because the virtual address space had become too fragmented, which of course would not solely be a result of unmapping. Or maybe that fragmentation is what you're referring to, and I'm just reading too much into that sentence.
Every OS nowadays has mmap, by some spelling, and they all support hugepages, likewise. A program that doesn't use its host OS doesn't, generally, do much.
I have a large fairly successful cross-platform program that does a hell of a lot. All of our interactions with the OS are wrapped, either by libstdc++ or by the portability library that we use.
We would never allow a direct call to mmap() from the main application code ... we don't even allow direct calls to most of the POSIX API since we have to run on Windows too (without WSL or whatever MS' current POSIX layer is).
"By some spelling" is precisely why almost all programmers are better off using "new X[N]" than mmap, unless you actually think that code like this is good:
#ifdef __APPLE__ #define LOCAL_HUGETLB 0 #else #define LOCAL_HUGETLB MMAP_HUGETLB #endif ...
mmap (..., foo|bar|LOCAL_HUGETLB)
This cannot be a serious suggestion regarding the recommended way to allocate memory.
I mean, I'm fairly sure that's the problem. (Plus a lot of systems support C++ but not virtual memory…)
Here on Windows, the OS doesn’t overcommit and the first new[] call actually allocates these pages (possibly in a page file).
Also, if the requested size is large enough (>512kb on Win32, >1MB on Win64), the OS guarantees zero initialization despite C++ doesn’t: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi...
There is no particular reason you can't allocate 500GB in 1 cycle. Just get rid of all memory management.
It seems silly to answer a question about C++ in operations per second, besides.
Is this an actual thing? You can force reification of overcommitted memory just by calling operator() on it?
char *buf = new char[s]();
Put into Compiler Explorer: https://gcc.godbolt.org/z/QAs9gz
However, doing this is a very strong code smell. At the very least, using `new` and assigning to a raw pointer is a sign that the C++ developer is managing memory manually and is likely to hit a lot of problems including memory leaks or segmentation faults. Also many would forget that this is calling `operator new[]()`, not `operator new()` and might confuse with placement-new `operator new(...)` or `operator new[](...)`. And the developer might also forget that `new` could throw an exception... [0]The developer should instead be using, at a minimum, a `std::unique_ptr<char[]>` [1]. Or, IMO, a `std::vector<char>` which reminds the developer not only of the pointer but also of the count of bytes which have been allocated (.capacity()) and also of the valid range which has been initialized (.size()).
IMO, if the developer wanted a pointer to a byte array then it's a lot easier to use `malloc()` than trying to remember all the different ways you can get screwed by `operator new`:
std::unique_ptr<char, std::function<void(char*)>> m{
(char*)std::malloc(count), std::free
};
[0]: https://en.cppreference.com/w/cpp/language/new std::make_unique<std:::array<char, SIZE>>()
If you don't know how big you want the byte array make a vector. std::vector<char>(size)
It's baffling to me how many C++ developers don't use the standard collections. The only one that really sucks is unordered_map.There is no need to badmouth all c++ developers. Plenty of us can keep our memory straight just fine. just because you don't like pointers, does not mean the rest of us can't use them perfectly safely.
I surely have my share of C++ pointer mistakes done since I got hold of C++ARM and Turbo C++, after years of Assembly and Turbo Pascal programming.
Not only C++, but any other language with manual memory management, because keeping track of where everything is going manually, just doesn't scale.
IMO the debate about whether programmers can safely handle bare pointers is over. They can't. The only question is whether smart pointers help enough to make the extra line noise worth it.
I actually use pointers quite safely. I just no longer see a need to ever return an allocation from `new` to a raw pointer unless I'm implementing my own pointer class.
Herb Sutter's correct [0]. `std::unique_ptr` or `std::shared_ptr` or some other pointer container should __always__ hold new-allocated objects. Just like you should __always__ wear your seatbelt. It's no danger to you or anyone else, it's slightly inconvenient, and it saves a metric ton of headaches about "what if?". Because in reality a pointer container explicitly marks the scope of the allocation and if you're not wanting to use C++'s scoping rules then why are you using C++?
I paid for lessons on it. The std:: stuff didn't exist, templates were a recent invention, and people were starting to agree that putting the overload keyword everywhere was bad style. Mostly, people were just excited about // comments and cout.
I keep hearing about how C++ is supposed to be done, and it seems to change every year. I don't know what people do with the older code as it becomes unfashionable with "very strong code smell". Updating it is risky busywork, kind of like porting to Python 3, with potential for serious bugs. Leaving it in place will make developers differently unhappy.
It isn't easy getting everybody on a team to agree on what subset of C++ is OK to use. People will sneak in their must-have feature. You'd have better luck getting agreement between emacs and vi.
Failing to keep up with evolving languages is called stagnation. Everyone is free to stagnate, but I do not advise it.
You use the subset of the language supported by the compiler you have. Code using newer features is better because the new features were added for sound engineering reasons, not just to be different.
That is, it uses "allocate" to mean "make ready for immediate use with no further (lazy) processing". Another perfectly reasonable definition would be "guarantee to be possible to use".
The distinction would arise on, for example, a system which doesn't over-commit memory but also doesn't fault pages in upon allocation. On such a system, allocation might give you a rock solid guarantee that you can write to (and read from) that memory but it wouldn't give you any guarantee about how fast or slow that would happen on initial access.
Personally, I prefer the second definition, but that's not really the point. The point is to be clear and avoid confusion.
New/delete like this are for smallish general purpose allocation, for example of objects, where we want to keep the code at a fairly high level in C++ and not think about low-level concepts like "bytes" or allocation mechanics. Or performance.
Conversely, an app written in C++ that needs huge swaths memory for manipulating raw bytes and needs high-performance would not likely use new char[] or calloc/malloc directly at all. It would directly interface with the OS via mmap or indirectly via some domain relevant library, for example, OpenCV.
We might even still exclusively use the C++ language to write the low-level portions of the code, but if you need to interface with the OS in specific controlled ways or write performance oriented code, you are not going to do it using only high-level C++ operations. You are going to call exactly what you need to interface with the system directly. If you want mmap(), you'd just call mmap() from C++. If you want sbrk(), you'd call sbrk(). If you don't like the system new/delete and malloc/free, you could use something like dlmalloc and and even remap new/delete to it, or to some custom slab allocator.
Secondly, as pointed out by others, the author isn't benchmarking C++. He's benchmarking glibc malloc and the Linux mmap. We'd expect a C program using malloc/calloc to have exactly the same timings.
Thirdly, fallacious reasoning about initialization. If your app needs to allocate (for some ??? reason) 32 GB of memory, you would NOT automatically zero it first, unless you actually needed it to be zero, or you wanted your app to waste a bunch of time. Unnecessary zeroing huge arrays is not required for good security. We're mostly benchmarking memset here, not even malloc/mmap. So it's completely apples/oranges to compare mmap benchmark with a zerofill benchmark. I almost expect a follow-up article that points out accessing x[i] is much faster when x is a raw array of integers than when x is a std::map of strings.
Now read the footnotes. The author is misunderstanding what "idomatic C++" means. RAII is the best answer I can come up with for idiomatic - C++ doesn't have a built-in guard concept so we creatively mis-use constructors and destructors to get a similar result.
Footnote 2 proves the author knows bupkis about how C++ or any part of the system actually works. He's semi-admitting as much.
I'd hope for much better from a CS professor. Stick to benchmarking this:
for(i=0; i<1E9; ++i);This makes me think if the whole question is really about C++ or the OS implementation for memory allocations. This could have little to do with C++.
On a bare-metal system, malloc/sbrk (and thus new) can be something as simple as:
uint32_t retptr = heap;
heap += size;
return retptr;Yes, you could add some platform and type specific speedups, it ends up a big mess.
In C++20 you get uninitialized allocation functions which do not have to initialize.
Very similar on Windows. In modern CRT, malloc is a thin wrapper over HeapAlloc WinAPI. That one has a threshold (512kb or 1MB depending on 32- or 64-bit process) where it switches to VirtualAlloc.