Skip to content

Commit a28e42a

Browse files
authored
Merge pull request #351 from sshanks-kx/gc
improve docs relating to memory management
2 parents 73ac598 + f1dee17 commit a28e42a

File tree

2 files changed

+157
-91
lines changed

2 files changed

+157
-91
lines changed

docs/basics/syscmds.md

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -315,41 +315,41 @@ q){x where x like"ht??"}system"f .h"
315315
## `\g` (garbage collection mode)
316316

317317
```syntax
318-
\g mode
318+
\g / current garbage-collection mode
319+
\g mode / set garbage-collection mode
319320
```
320321

321-
Show or set garbage-collection mode.
322-
The default mode is 0.
322+
Show or set garbage-collection mode. The default mode is 0 (deferred). Setting the garbage-collection mode will automatically call [`.Q.gc[]`](../ref/dotq.md#gc-garbage-collect) after setting the provided value.
323+
324+
Q manages its own thread-local heap. Objects in q use reference counting. As soon as there are no references to an object, its memory is eligable to be returned to the heap.
323325

324326
0 (deferred)
325327

326-
: returns memory to the OS when either `.Q.gc[]` is called or an allocation fails, hence has a performance advantage, but can be more difficult to dimension or manage memory requirements.
328+
: Returns memory to the thread-local heap. Will subsequently return memory to the OS when either `.Q.gc[]` is called or an allocation fails, hence has a performance advantage, but can be more difficult to dimension or manage memory requirements.
327329

328330
1 (immediate)
329331

330-
: returns (certain types of) memory to the OS as soon as no longer referenced; has an associated performance overhead.
331-
332-
Q manages its own thread-local heap.
333-
334-
Vectors always have a capacity and a used size (the count).
335-
336-
There is no garbage since q uses reference counting. As soon as there are no references to an object, its memory is returned to the heap.
337-
338-
During that return of memory, q checks if the capacity of the object is ≥64MB. If it is and `\g` is 1, the memory is returned immediately to the OS; otherwise, the memory is returned to the thread-local heap for reuse.
339-
340-
Executing [`.Q.gc[]`](../ref/dotq.md#qgc-garbage-collect) additionally attempts to coalesce pieces of the heap into their original allocation units and returns any units ≥64MB to the OS.
341-
342-
Since V3.3 2015.08.23 (Linux only) unused pages in the heap are dropped from RSS during `.Q.gc[]`.
332+
: As memory is returned to the thread-local heap, if the object is ≥64MB then the memory is returned to the OS instead. This has an associated performance overhead. As per `defered mode`, memory used by the heap may be subsequently returned to the OS when either `.Q.gc[]` is called or an allocation fails.
343333

344334
When q is denied additional address space from the OS, it invokes `.Q.gc[]` and retries the request to the OS.
345335
If the subsequent attempt fail, the request exits with [`'wsfull`](../basics/errors.md#wsfull).
346336

347-
When secondary threads are configured and `.Q.gc[]` is invoked in the main thread, `.Q.gc[]` is automatically invoked in each secondary thread.
348-
If the call is instigated in a secondary thread, it affects that thread’s local heap only.
349-
350337
!!! detail "Notes on the allocator"
351338

352339
Q’s allocator bins objects in power-of-two size categories, from 16b (e.g. an atom) to 64MB.
340+
341+
In this example, various vectors of longs (8 bytes per long) are created of different sizes using [`til`](../ref/til.md).
342+
The memory used for the operation is shown via [`\ts`](#ts-time-and-space). Note that more bytes are reported
343+
that only the pure vector size due to other house keeping, for example the type information.
344+
```q
345+
q)\ts til 800 / 800*8=6400, needs a 2^13=8192 byte slab (too big for a 2^12=4096 byte slab)
346+
0 8368
347+
q)\ts til 1000 / 1000*8=8000, needs a 2^13=8192 byte slab (memory same as smaller vector above)
348+
0 8368
349+
q)\ts til 1200 / 1200*8=9600, cant fit in a 2^13=8192 bytes slab, needs 2^14=16384 byte slab
350+
0 16560
351+
```
352+
353353
If there is already a slab in the object category’s freelist, it is reused.
354354
If there are no available slabs, a larger slab is recursively split in two until the needed category size is reached.
355355
If there are no free slabs available, a new 64MB slab is requested from the system.
@@ -365,15 +365,61 @@ If the call is instigated in a secondary thread, it affects that thread’s loca
365365

366366
split slab
367367

368-
: Suppose that at some point q needed a 32MB allocation. It requested a new 64MB slab from the OS, split it in half, used and freed the object, and returned the two 32MB slabs to the freelist. Now if q needs to allocate 64MB, it will have to make another request to the OS. running `.Q.gc` would attempt to coalesce these two 32MB slabs together back into one 64MB, which would allow it to be returned to the OS (or reused for larger allocations, if the resulting slab is <64MB).
368+
: Suppose that at some point q needed a 32MB allocation. It requested a new 64MB slab from the OS, split it in half, used and freed the object, and returned the two 32MB slabs to the freelist. Now if q needs to allocate 64MB, it will have to make another request to the OS. When `.Q.gc` is called (or an allocation fails), it would attempt to coalesce these two 32MB slabs together back into one 64MB, which would allow it to be returned to the OS (or reused for larger allocations, if the resulting slab is <64MB).
369369

370370
leftover objects
371371

372-
: If most of the objects allocated from a 64MB slab are freed but one remains, the slab still cannot be returned to the OS (or coalesced). In this case, `.Q.gc` notifies the OS that the physical memory backing the unused pages in the block can be reclaimed.
372+
: If most of the objects allocated from a 64MB slab are freed but one remains, the slab still cannot be returned to the OS (or coalesced).
373373

374+
The following example shows freeing an object ≥64MB in `deferred` mode, while inspecting memory usage via [`.Q.w[]`](../ref/dotq.md##w-memory-stats):
375+
```q
376+
q).Q.w[]`used`heap / original memory used and memory reserved by kdb+ at time of test
377+
371552 67108864
378+
q)a:til 10000000 / need memory ≥64MB to store value
379+
q).Q.w[]`used`heap / heap (memory reserved by kdb+) has grown, and used memory grown from the heap has grown
380+
134589328 201326592
381+
q)a:1 / variable assigned different value, old value no longer used
382+
q).Q.w[]`used`heap / heap (memory reserved by kdb+) hasn't reduced as it is kept for future use, used memory has reduced
383+
371616 201326592
384+
q)a:til 10000000 / need memory ≥64MB to store value again
385+
q).Q.w[]`used`heap / heap memory (no increase) as memory used has been taken from the available heap
386+
134589328 201326592
387+
```
388+
The same example will differ when using `immediate` mode, by returning memory to the OS (as the object free'd is greater than 64MB):
389+
```q
390+
q).Q.w[]`used`heap / original memory used and memory reserved by kdb+ at time of test
391+
371648 67108864
392+
q)a:til 10000000 / need memory ≥64MB to store value
393+
q).Q.w[]`used`heap / heap (memory reserved by kdb+) has grown, and used memory from the heap has grown
394+
134589424 201326592
395+
q)a:1 / variable assigned different value, old value no longer used
396+
q).Q.w[]`used`heap / heap (memory reserved by kdb+) has reduced, it has been returned to OS
397+
371712 67108864
398+
q)a:til 10000000 / need memory ≥64MB to store value again
399+
q).Q.w[]`used`heap / heap memory has increased (requested from OS) as memory used is more than whats available to use in heap
400+
134589328 201326592
401+
```
402+
`Immediate mode` will not return the memory to the OS when several objects less than 64MB each are freed, even though their sum may be more than 64MB.
403+
In this situation, `immediate` and `deferred` mode operate identically by adding the freed memory to the heap for future use.
404+
405+
The following examples shows this effect when running in `immediate mode`.
406+
No memory is returned to the OS on freeing the objects, and only when [`.Q.gc[]`](../ref/dotq.md#gc-garbage-collect) is run is the memory coalesced and freed.
407+
```q
408+
q).Q.w[]`used`heap / original memory used and memory reserved by kdb+ at time of test
409+
371648 67108864
410+
q)v:`a`b`c`d`e`f`g`h`i`j / create a list of 10 variable names to use
411+
q){set[x;til 1000000]} each v / create a global variable using each of the names in v, each containing 1000000 longs
412+
q).Q.w[]`used`heap / heap (memory reserved by kdb+) has grown, and used memory from the heap has grown
413+
84258096 134217728
414+
q)![`.;();0b;v] / delete all the variables and their contents
415+
q).Q.w[]`used`heap / used memory has been reduced, but none of the heap memory has returned to the OS
416+
371824 134217728
417+
q).Q.gc[] / running garbage collection freed over 64MB
418+
67108864
419+
```
374420

375421
:fontawesome-solid-book-open:
376-
[Command-line option `-g`](cmdline.md#-g-garbage-collection)
422+
[Command-line option `-g`](cmdline.md#-g-garbage-collection) (garbage collection mode), [Command-line parameter `-w`](../basics/cmdline.md#-w-workspace) (workspace memory limit), [System command `\w`](../basics/syscmds.md#w-workspace) (memory stats and workspace memory limit)
377423
<br>
378424
:fontawesome-solid-street-view:
379425
_Q for Mortals_
@@ -789,10 +835,10 @@ q){x where x like"????"}system"v .h"
789835
With no parameter, returns current memory usage, as a list of 6 long integers.
790836

791837
```txt
792-
0 number of bytes allocated
793-
1 bytes available in heap
838+
0 number of bytes from the heap that are currently in use
839+
1 heap size in bytes
794840
2 maximum heap size so far
795-
3 limit on thread heap size, from -w command-line option
841+
3 limit on thread heap size, from -w command-line option or \w system command
796842
4 mapped bytes
797843
5 physical memory
798844
```
@@ -819,8 +865,8 @@ The utility [`.Q.w`](../ref/dotq.md#w-memory-stats) formats all this information
819865

820866
**Run-time increase**
821867
Since 2017.11.06, `\w` allows the workspace limit to be increased at run-time, if it was initialized via the
822-
[`-w` command-line option](cmdline.md#-w-workspace).
823-
E.g. `system "w 128"` sets the `-w` limit to the larger of 128 MB and the current setting and returns it.
868+
[`-w` command-line option](cmdline.md#-w-workspace). For example `\w 128` sets the limit to 128MB if the `-w` command line option was specified
869+
with a smaller value. The operation will return the current setting in bytes.
824870

825871
If the system tries to allocate more memory than allowed, it signals `-w abort` and terminates with exit code 1.
826872

@@ -858,9 +904,9 @@ q)value each ("\\d .m";"\\w";"\\d .";"\\w")
858904
```
859905

860906
:fontawesome-solid-book-open:
861-
[`-w` command-line option](cmdline.md#-w-workspace)<br>
907+
[`-w` workspace command-line option](cmdline.md#-w-workspace), [`\g`](#g-garbage-collection-mode) (garbage-collection mode)<br>
862908
:fontawesome-solid-book:
863-
[`.m` namespace](../ref/dotm.md)
909+
[`.m` namespace](../ref/dotm.md) (DAX-enabled filesystems)
864910

865911

866912
## `\W` (week offset)

docs/ref/dotq.md

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -923,94 +923,114 @@ q)r1~r2
923923
.Q.gc[]
924924
```
925925

926-
Returns the amount of memory that was returned to the OS.
926+
Run garbage-collection and returns the amount of memory that was returned to the OS.
927927
<!-- (Since V2.7 2010.08.05, enhanced with coalesce in V2.7 2011.09.15, and executes in secondary threads since V2.7 2011.09.21) -->
928+
It attempts to coalesce pieces of the heap into their original allocation units and returns any units ≥64MB to the OS.
929+
Refer to [`\g`](../basics/syscmds.md#g-garbage-collection-mode) (garbage collection mode) for details on how memory is created on the heap.
928930

929-
!!! detail "How it works: reference counting and [buddy memory allocation](https://en.wikipedia.org/wiki/Buddy_memory_allocation)"
931+
When secondary threads are configured and `.Q.gc[]` is invoked in the main thread, `.Q.gc[]` is automatically invoked in each secondary thread.
932+
If the call is instigated in a secondary thread, it affects that thread’s local heap only.
930933

934+
Example of garbage collection in the default `deferred` mode, using [`.Q.w[]`](#w-memory-stats) to view memory stats:
931935
```q
932-
q)a:til 10000000
933-
q).Q.w[]
934-
used| 67233056
935-
heap| 134217728
936-
peak| 134217728
936+
q)a:til 10000000 / create an object that is ≥64MB
937+
q).Q.w[] / view current heap size and how many bytes used of the heap (all objects plus previously allocated object)
938+
used| 134589136
939+
heap| 201326592
940+
peak| 201326592
937941
wmax| 0
938942
mmap| 0
939-
syms| 534
940-
symw| 23926
941-
q).Q.gc[]
943+
mphy| 17179869184
944+
syms| 689
945+
symw| 37406
946+
q).Q.gc[] / garbage collection doesnt return any memory to OS
942947
0
943-
q)delete a from `.
948+
q)delete a from `. / delete the original object, placing it on the heap
944949
`.
945-
q).Q.gc[]
946-
67108864
947-
q).Q.w[]
948-
used| 128768
950+
q).Q.w[] / used memory has decreased, heap remains the same
951+
used| 371376
952+
heap| 201326592
953+
peak| 201326592
954+
wmax| 0
955+
mmap| 0
956+
mphy| 17179869184
957+
syms| 690
958+
symw| 37436
959+
q).Q.gc[] / garbage collection has returned 134217728 to the OS from the heap
960+
134217728
961+
q).Q.w[] / heap size has reduced, while used memory remains the same
962+
used| 371376
949963
heap| 67108864
950-
peak| 134217728
964+
peak| 201326592
951965
wmax| 0
952966
mmap| 0
953-
syms| 535
954-
symw| 23956
967+
mphy| 17179869184
968+
syms| 690
969+
symw| 37436
955970
```
956971

957-
Note that memory can become fragmented and therefore difficult to release back to the OS.
972+
Depending on your data, memory can become fragmented and therefore difficult to release back to the OS. The following demonstrates an example:
958973

959974
```q
960-
q)v:{(10#"a";10000#"b")}each til 10000000;
961-
q).Q.w[]
962-
used| 164614358256
963-
heap| 164752261120
964-
peak| 164752261120
975+
q).Q.w[] / initial memory stats
976+
used| 371360
977+
heap| 67108864
978+
peak| 67108864
965979
wmax| 0
966980
mmap| 0
967-
mphy| 270538350592
968-
syms| 569
969-
symw| 24934
970-
q).Q.gc[]
971-
134217728
972-
q).Q.w[]
973-
used| 164614358256
974-
heap| 164618043392
975-
peak| 164752261120
981+
mphy| 17179869184
982+
syms| 689
983+
symw| 37406
984+
q)v:{(10#"a";10000#"b")}each til 1000000; / create 1000000 rows, each containing 2 elements of 10 chars and 10000 chars
985+
q).Q.w[] / both heap and used memory has grown
986+
used| 16456760016
987+
heap| 16508780544
988+
peak| 16508780544
976989
wmax| 0
977990
mmap| 0
978-
mphy| 270538350592
979-
syms| 570
980-
symw| 24964
981-
q)v:v[;0] / just retain refs to the small char vectors of "aaaaaaaa"
982-
q)/the vectors of "bbb.."s will come from the same memory chunks
983-
q)/so can't be freed
984-
q).Q.gc[]
985-
134217728
986-
q).Q.w[]
987-
used| 454358256
988-
heap| 164618043392
989-
peak| 164752261120
991+
mphy| 17179869184
992+
syms| 689
993+
symw| 37406
994+
q).Q.gc[] / garbage collection has found no slab of contiguous unused memory of ≥64MB to free
995+
0
996+
q)v:v[;0] / change v to 1000000 rows, each only containing the 1st element of 10 chars (2nd element removed)
997+
q).Q.w[] / used memory has decreased, heap remains the same
998+
used| 40760016
999+
heap| 16508780544
1000+
peak| 16508780544
9901001
wmax| 0
9911002
mmap| 0
992-
mphy| 270538350592
993-
syms| 570
994-
symw| 24964
995-
q)v:-8!v;0N!.Q.gc[];v:-9!v;.Q.w[] / serialize, release, deserialize
996-
164483825664 / amount freed by gc
997-
used| 454358848
998-
heap| 738197504
999-
peak| 164886478848
1003+
mphy| 17179869184
1004+
syms| 690
1005+
symw| 37436
1006+
q).Q.gc[] / garbage collection has found no contiguous unused memory of ≥64MB to free
1007+
0
1008+
q)v:-8!v / convert v into its serialised form, return vector used by v to heap
1009+
q).Q.gc[] / garbage collection now found unused contiguous memory slab ≥64MB to return to OS
1010+
16374562816
1011+
q)v:-9!v / convert serialised form of v back to its original state
1012+
q).Q.w[] / used memory remains the same as before, but heap has reduced
1013+
used| 40760016
1014+
heap| 134217728
1015+
peak| 16508780544
10001016
wmax| 0
10011017
mmap| 0
1002-
mphy| 270538350592
1003-
syms| 570
1004-
symw| 24964
1018+
mphy| 17179869184
1019+
syms| 690
1020+
symw| 37436
10051021
```
10061022

1007-
So if you have many nested data, e.g. columns of char vectors, or much grouping, you may be fragmenting memory quite heavily.
1023+
If you have nested data, e.g. columns of char vectors, or much grouping, you may be fragmenting memory.
1024+
1025+
Since V3.3 2015.08.23 (Linux only) unused pages in the heap are dropped from RSS during `.Q.gc[]`.
10081026

10091027
Since 4.1t 2022.07.01, `.Q.gc[0]` can be used to perform a subset of operations performed by `.Q.gc[]` (i.e. only return unused blocks >= 64MB to os).
10101028
This has the advantage of running return faster than `.Q.gc[]`, but with the disadvantage of not defragmenting unused memory blocks of a smaller size (therefore may not free as much unused memory).
10111029

10121030
:fontawesome-solid-hand-point-right:
1013-
[`.Q.w`](#w-memory-stats) (memory stats)
1031+
[`.Q.w`](#w-memory-stats) (memory stats),
1032+
[`\g`](../basics/syscmds.md#g-garbage-collection-mode) (garbage collection mode),
1033+
[`\w`](../basics/syscmds.md#w-workspace) (workspace)
10141034

10151035

10161036
## `gz` (GZip)
@@ -2118,7 +2138,7 @@ q)@[get;"select from tt";-2@]; / no error
21182138
.Q.w[]
21192139
```
21202140

2121-
Returns the memory stats from [`\w`](../basics/syscmds.md#w-workspace) into a more readable dictionary.
2141+
Returns the memory stats from [`\w`](../basics/syscmds.md#w-workspace) into a more readable dictionary. Refer to [`\w`](../basics/syscmds.md#w-workspace) for an explaination of each statistic.
21222142

21232143
```q
21242144
q).Q.w[]
@@ -2135,10 +2155,10 @@ symw| 25436
21352155
:fontawesome-solid-hand-point-right:
21362156
[`.Q.gc`](#gc-garbage-collect) (garbage collect)<br>
21372157
:fontawesome-solid-book-open:
2138-
[Command-line parameter `-w`](../basics/cmdline.md#-w-workspace)
2158+
[Command-line parameter `-w`](../basics/cmdline.md#-w-workspace) (workspace memory limit)
21392159
<br>
21402160
:fontawesome-solid-book-open:
2141-
[System command `\w`](../basics/syscmds.md#w-workspace)
2161+
[System command `\w`](../basics/syscmds.md#w-workspace) (memory stats and workspace memory limit)
21422162

21432163

21442164
## `Xf` (create file)

0 commit comments

Comments
 (0)