| 1 | # One Way Allocator |
| 2 | |
| 3 | This is a very fast single-threaded-only memory allocator, that minimized system calls |
| 4 | when a lot of memory allocations needs to be made to perform a task, which all of them |
| 5 | can be freed together when the task finishes. |
| 6 | |
| 7 | It has been designed to be used for netdata context queries. |
| 8 | |
| 9 | For netdata to perform a context query, it builds a virtual chart, a chart that contains |
| 10 | all the dimensions of the charts having the same context. This process requires allocating |
| 11 | several structures for each of the dimensions to attach them to the virtual chart. All |
| 12 | these data can be freed immediately after the query finishes. |
| 13 | |
| 14 | ## How it works |
| 15 | |
| 16 | 1. The caller calls `ONEWAYALLOC *owa = onewayalloc_create(sizehint)` to create an OWA. |
| 17 | Internally this allocates the first memory buffer with size >= `sizehint`. |
| 18 | If `sizehint` is zero, it will allocate 1 hardware page (usually 4kb). |
| 19 | No need to check for success or failure. As with `mallocz()` in netdata, a `fatal()` |
| 20 | will be called if the allocation fails - although this will never fail, since Linux |
| 21 | does not really check if there is memory available for `mmap()` calls. |
| 22 | |
| 23 | 2. The caller can then perform any number of the following calls to acquire memory: |
| 24 | - `onewayalloc_mallocz(owa, size)`, similar to `mallocz()` |
| 25 | - `onewayalloc_callocz(owa, nmemb, size)`, similar to `callocz()` |
| 26 | - `onewayalloc_strdupz(owa, string)`, similar to `strdupz()` |
| 27 | - `onewayalloc_memdupz(owa, ptr, size)`, similar to `mallocz()` and then `memcpy()` |
| 28 | |
| 29 | 3. Once the caller has done all the work with the allocated buffers, all memory allocated |
| 30 | can be freed with `onewayalloc_destroy(owa)`. |
| 31 | |
| 32 | ## How faster it is? |
| 33 | |
| 34 | On modern hardware, for any single query the performance improvement is marginal and not |
| 35 | noticeable at all. |
| 36 | |
| 37 | We performed the following tests using the same huge context query (1000 charts, |
| 38 | 100 dimensions each = 100k dimensions) |
| 39 | |
| 40 | 1. using `mallocz()`, 1 caller, 256 queries (sequential) |
| 41 | 2. using `mallocz()`, 256 callers, 1 query each (parallel) |
| 42 | 3. using `OWA`, 1 caller, 256 queries (sequential) |
| 43 | 4. using `OWA`, 256 callers, 1 query each (parallel) |
| 44 | |
| 45 | Netdata was configured to use 24 web threads on the 24 core server we used. |
| 46 | |
| 47 | The results are as follows: |
| 48 | |
| 49 | ### sequential test |
| 50 | |
| 51 | branch|transactions|time to complete|transaction rate|average response time|min response time|max response time |
| 52 | :---:|:---:|:---:|:---:|:---:|:---:|:---:| |
| 53 | `malloc()`|256|322.35s|0.79/sec|1.26s|1.01s|1.87s |
| 54 | `OWA`|256|310.19s|0.83/sec|1.21s|1.04s|1.63s |
| 55 | |
| 56 | For a single query, the improvement is just marginal and not noticeable at all. |
| 57 | |
| 58 | ### parallel test |
| 59 | |
| 60 | branch|transactions|time to complete|transaction rate|average response time|min response time|max response time |
| 61 | :---:|:---:|:---:|:---:|:---:|:---:|:---:| |
| 62 | `malloc()`|256|84.72s|3.02/sec|68.43s|50.20s|84.71s |
| 63 | `OWA`|256|39.35s|6.51/sec|34.48s|20.55s|39.34s |
| 64 | |
| 65 | For parallel workload, like the one executed by netdata.cloud, `OWA` provides a 54% overall speed improvement (more than double the overall |
| 66 | user-experienced speed, including the data query itself). |