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
//! A way for the program to say how freed memory goes back to the system, and for a load to ask.
//!
//! The allocator belongs to the binary and not to the library, so the library cannot call into it.
//! What it can do is say when a good moment is, and let the binary decide what that means. The shell
//! built on glibc's allocator registers `malloc_trim`, because glibc keeps what a thread frees in
//! that thread's arena and hands back only what sits at the top of it. A load allocates and frees a
//! stripe's buffers on every worker, over and over, and what it frees lands between blocks still
//! held, so the process grows long after what it holds has stopped growing. On the 10 million row
//! `hits` load on server3 the peak was 2.76 to 2.98 GB resident, of which the load accounted for
//! 1.32 GB, and 1.63 to 1.69 GB with a release after every stripe and every column closed.
//!
//! Nothing is registered by default, which makes [`release`] a load of one pointer. A library user
//! who wants the same can register their own.
use OnceLock;
use ;
use Instant;
/// What [`release`] calls, when the program has said.
static RELEASE: = new;
/// When the process started, as far as [`release`] is concerned, so a time fits in an atomic.
static START: = new;
/// Milliseconds after [`START`] of the last call that went through, or zero for none yet.
static LAST: AtomicU64 = new;
/// The least time between two calls that go through.
///
/// A trim walks every arena and takes each one's lock while it does, so six workers finishing
/// stripes at once should pay for one of them and not six. Half a second is well under the few
/// seconds a worker takes to fill a stripe, so a load still trims about once a stripe.
const EVERY_MS: u64 = 500;
/// Sets what [`release`] does, once. A second call is ignored, since the allocator does not change
/// while the process runs.
/// Asks the allocator to give back what it is holding free, if the program said how and nobody
/// else asked in the last half second.
///
/// For a place that has just dropped a lot, like a load that has written a stripe. It is a hint and
/// never fails, and the caller does not wait for anybody else's call to finish.