-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.txt
326 lines (245 loc) · 12 KB
/
README.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
Memorypa
========
Memorypa is a memory pooling agent written in C/C++ that targets the
GNU and Microsoft Visual C++ compiler collections. Include, compile,
and link your program to Memorypa to improve performance. You can even
safely override the C standard allocation functions.
Requirements
------------
- GCC or Microsoft Visual Studio.
- A standard 32-bit or 64-bit architecture/platform.
Rushing Into This
-----------------
Take a quick look at the compiler commands in the build
scripts. They're literally one line for each example. The main use
cases are "example_standard.c" and "example_with_overriding.c".
Look at "example_profiler.c" to understand how to measure your
program's memory usage.
Look at "example_standard.c" for basic usage.
Look at "example_with_overriding.c" for overriding/replacing
allocation functions.
Quick Start for GCC
-------------------
The core Memorypa code is isolated to "memorypa.h" and "memorypa.c".
For 64-bit GCC, compile and test:
./clean.sh
./build_m64.sh
./bin/test_memorypa_c profile
./bin/test_memorypa_c
./bin/benchmark_c
./bin/benchmark_memorypa_c
./bin/example_profiler_c
./bin/example_standard_c
./bin/example_with_overriding_c
./bin/example_with_overriding_and_alignment_c
Similarly for 32-bit GCC:
./clean.sh
./build_m32.sh
./bin/test_memorypa_c32 profile
./bin/test_memorypa_c32
./bin/benchmark_c32
./bin/benchmark_memorypa_c32
./bin/example_profiler_c32
./bin/example_standard_c32
./bin/example_with_overriding_c32
./bin/example_with_overriding_and_alignment_c32
To link to any 64-bit external project, all you need is:
ls include/memorypa.h
ls lib64/libmemorypa.so
Or for 32-bit:
ls include/memorypa.h
ls lib32/libmemorypa.so
Quick Start for MSVC
--------------------
Once again, the core Memorypa code is isolated to "memorypa.h" and
"memorypa.c".
"memorypa_overrider.h" and "memorypa_overrider.c" form a tiny DLL used
solely for overriding/replacing allocation functions. Please note that
"memorypa_overrider.h", "memorypa_overrider.c", and
"memorypa_overrider.def" are NOT exhaustive. Currently they only cover
"malloc", "calloc", "realloc", and "free". I'm happy to add more
functions as needed but you can always add them yourself!
"build_win.cmd" was only tested with "cl" under built-in Visual Studio
command prompts. It's possible to compile through the IDE or a basic
command prompt as long as close attention is paid to its "cl"
commands.
To target a specific architecture, simply start the correct MSVC
command prompt. E.g. use "x64 Native Tools Command Prompt" for 64-bit,
and "x86 Native Tools Command Prompt" for 32-bit.
"cd" into the Memorypa folder and run:
clean.cmd
build_win.cmd
.\win\test_memorypa.exe profile
.\win\test_memorypa.exe
.\win\benchmark.exe
.\win\benchmark_memorypa.exe
.\win\example_profiler.exe
.\win\example_standard.exe
.\win\example_with_overriding.exe
.\win\example_with_overriding_and_alignment.exe
For basic usage, link to any external project with:
.\include\memorypa.h
.\win\memorypa.lib
.\win\memorypa.dll
For overriding/replacing various allocation functions, link using:
.\include\memorypa.h
.\include\memorypa_overrider.h
.\win\memorypa.lib
.\win\memorypa.dll
.\win\memorypa_overrider.lib
.\win\memorypa_overrider.dll
Features
--------
I encourage quick starters to look at the example files, and
enthusiasts to simply read "memorypa.h" and "memorypa.c" (there's not
a lot of code). Memorypa is a compact library that:
- Provides all the C standard dynamic memory allocation functions
prefixed with "memorypa_", i.e. "memorypa_malloc", "memorypa_calloc",
"memorypa_realloc", and "memorypa_free".
- All functions follow the standard.
- All functions have exactly the same parameters as their counterparts.
- The allocators do NOT return pointers with "native" alignment. See the
Warnings section.
- Provides variations of said functions with the prefix
"memorypa_aligned_" that return aligned pointers. This is purely for
compatibility. Basic bitwise math is used to waste space in a given
block so that the right pointer is produced.
- Provides profiler variations of ALL allocation functions.
- These are all passthrough functions. They directly use the allocation
functions defined in "memorypa_initializer_options" with significant
overhead.
- These are the only functions that affect the output of
"memorypa_profile_print". See "example_profiler.c".
- Obtains the pool configuration from the user's
"memorypa_initializer_options" definition.
- The definition should populate the arguments "functions" and
"sets_of_pool_options" as shown in the examples.
- The code that can be placed in this function is entirely up to the
user. The bundled examples have hardcoded configurations, but an
fopen/fread strategy for parsing a separate file is easily possible as
long as strings/arrays are initialized on the stack.
- Allows for perfectly safe execution while overriding the standard
allocation functions. See "example_with_overriding.c".
- Use of the profiler functions while overriding is also safe.
- The given Linux example overrides "malloc", "calloc", "realloc",
"free", and "posix_memalign". There are other allocation functions
that are not included here that may need to be overridden.
- The given Windows example depends on a second library
"memorypa_overrider.dll" and only overrides "malloc", "calloc",
"realloc", and "free". There are also other allocation functions that
may need to be overridden.
- Provides a global pool validator "memorypa_pools_are_invalid" to
validate the entire allocation. See the Warnings section about the
best approach to keeping things in top shape.
- Provides potentially useful, cross-platform side functions:
- "memorypa_get_thread_id" to get the current thread ID.
- "memorypa_mhash" for a decent multiplicative hash function that adapts
to the size of "size_t".
Design
------
Memorypa obtains from the operating system literally all its pool
memory literally once. The memory is calculated from the pool
configuration right on the first call to Memorypa. So, the pool
configuration must be established and optimized on separate runs of
the target program. In other words, determine the target program's
peak memory usage using the Memorypa profiling functions, then run it
with the actual pooling functions.
Internalizing memory management this way is not innovative by any
stretch. Peak performance requires eliminating the overhead of context
switches, page faults, and so on caused by repeated use of the
built-ins. And experienced programmers often have no trouble
implementing simple allocators on-the-fly to do just that.
So why Memorypa?
1) Thread safety is performant. Each pool has its own mutex, and the
mutexes are live locking. Locking is extremely short and never
explicitly yeilds execution to another process.
2) Memory efficiency is designed carefully around powers of
two. Allocations are made very quickly using fast and simple
calculations.
3) The entire suite of functions can be used while overriding the
built-in allocation functions. Define your own "malloc" and use
"memorypa_malloc" within it. Memorypa was tested against many
unexpected situations, like when "malloc" is invoked before "main"
begins, or "malloc" is called recursively. This all means that
Memorypa will safely take control of "malloc" from any of the target
program's external libraries, not just the program itself.
4) It's portable and lightweight. Memorypa currently targets the GNU and
Visual Studio compilers, and there'll be more if things pick up.
Warnings
--------
The Memorpa profiling functions were intentionally separated from the
actual pooling functions to limit maintenance issues. Use a macro, a
wrapper function, or a function pointer to nickname the Memorypa
functions, then spread that nickname around your code. That way you
can effortlessly switch between profiling and pooling.
Some programs/libraries expect the C standard allocators to return
pointers that are multiples of 4 or 8 or something. The Memorypa pools
live in one massive, contiguous block of memory and maximizing its
efficiency requires this convention to be abandoned. In other words,
the pooling allocation functions that don't specify alignment return
multiples of... anything. However, the profiling functions defer to
the "malloc" and "free" set in the configuration so those will
obviously do whatever the OS is doing. In any case, see
"example_with_overriding_and_alignment.c" for a workaround using the
explicit alignment functions.
Because Memorypa is built around performance, there is basically zero
memory protection. That is in fact one of its optimizing
properties. The trade-off is that any incidents of memory corruption,
namely overflows/overruns, will cause all hell to break loose. The
bundled "memorypa_pools_are_invalid" can be used to incrementally hunt
down some issues but the MUCH better approach is to eject Memorypa and
use established debuggers. The GNU debugger (GDB), Valgrind, and the
built-in Visual Studio debugger are all perfectly good. Once the
target program is rock solid, there will be no problems using
Memorypa. And if that turns out to be a lie (ha!), the build scripts
can be modified to include debugging symbols. So... debug!!!
There is a hidden performance penalty when *unnecessarily* using
"calloc". Built-in allocators lazily allocate memory such that if
"calloc" is called but the target memory is never read or modified,
then no allocation actually occurs. This has no consequences when
using "memorypa_malloc" or "memorypa_realloc", but "memorypa_calloc"
must wipe the memory before providing it. Memorypa has no way of
determining usage ahead of time, so this must be a full wipe every
time. On the other hand, the standard "calloc" only needs to perform
this wipe when the memory is acted upon, and it does so only in
"pages". There are even optimizations that allow it to skip the wipe
entirely. So, if the target program uses "calloc" without actually
doing anything with the provided memory, expect some disappointment
when switching to "memorypa_calloc". Please allocate memory
competently.
The ability to pad the slab size of each pool is very powerful. Pool n
is not stuck accepting slabs of maximum size 2^n - 1. If memory
efficiency is unacceptable, especially at higher pools, use padding to
bucket those allocations into lower pools, saving space.
The "alignment" argument in the "aligned" functions MUST be a power of
2, with a maximum of 32768. Using anything else will result in
undefined behavior.
And finally, as mentioned previously, the given examples for
overriding built-in allocators are currently NOT exhaustive. One might
think "malloc", "calloc", "realloc", and "free" are all there is, but
there are in fact a bunch of other non-standard functions unique to
each OS ("posix_memalign" is in there). These tend to be allocators
that need to interact with "free". These functions simply need to be
located and overridden as well. The examples will be patched as
needed.
Contributing
------------
Please communicate changes over this project's GitLab/GitHub
pages. Bear in mind that the GitLab/GitHub pages are merely mirrors of
a separate "official" repository. Any accepted changes will get due
authorship and credit.
https://gitlab.com/nzeid/memorypa
https://github.com/nzeid/memorypa
License
-------
Copyright (c) 2019 Nader G. Zeid
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/gpl.html>.