mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
mm/zone: switch to linear-log bucketing
While we're here, also shrink some types.
This commit is contained in:
+144
-87
@@ -172,26 +172,17 @@ const excess_memory = ke.TunableType(
|
|||||||
"mm.zone.excess_mem",
|
"mm.zone.excess_mem",
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Every power-of-two-size from 8 to 2048 (inclusively).
|
/// The number of generic zones to generate.
|
||||||
const generic_zones_num = 9;
|
const generic_zones_num = 33; // Up to 8192 bytes
|
||||||
|
|
||||||
const generic_zone_names = [_][]const u8{
|
|
||||||
"zone-8",
|
|
||||||
"zone-16",
|
|
||||||
"zone-32",
|
|
||||||
"zone-64",
|
|
||||||
"zone-128",
|
|
||||||
"zone-256",
|
|
||||||
"zone-512",
|
|
||||||
"zone-1024",
|
|
||||||
"zone-2048",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 1/8th of a page.
|
/// 1/8th of a page.
|
||||||
const small_slab_size = 512;
|
const small_slab_size = 512;
|
||||||
|
|
||||||
/// Generic zones for power-of-two sizes from 8 to 2048.
|
/// Generic zones for general-purpose allocations.
|
||||||
var generic_zones: [generic_zones_num]Zone = undefined;
|
var generic_zones: [generic_zones_num]Zone = undefined;
|
||||||
|
const generic_sizes = generate_generic_sizes();
|
||||||
|
const generic_names = generate_generic_names(generic_sizes);
|
||||||
|
const generic_max_size = bucket_to_size(generic_zones_num - 1);
|
||||||
|
|
||||||
var magazines_initialized = false;
|
var magazines_initialized = false;
|
||||||
var all_zones: ?*Zone = null;
|
var all_zones: ?*Zone = null;
|
||||||
@@ -361,11 +352,9 @@ const MagazineList = struct {
|
|||||||
head: ?*Magazine,
|
head: ?*Magazine,
|
||||||
tail: ?*Magazine,
|
tail: ?*Magazine,
|
||||||
/// Number of magazines.
|
/// Number of magazines.
|
||||||
num: usize,
|
num: u32,
|
||||||
/// Minimum since last update
|
/// Minimum since last update
|
||||||
min: usize,
|
min: u32,
|
||||||
/// Weighted moving average of `min`, scaled by `wma_unit`.
|
|
||||||
wma: usize,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReusePolicy = enum {
|
const ReusePolicy = enum {
|
||||||
@@ -430,9 +419,9 @@ const Cpu = struct {
|
|||||||
/// Magazine used for frees.
|
/// Magazine used for frees.
|
||||||
free: ?*Magazine,
|
free: ?*Magazine,
|
||||||
/// Number of rounds in alloc magazine.
|
/// Number of rounds in alloc magazine.
|
||||||
alloc_rounds: usize,
|
alloc_rounds: u8,
|
||||||
/// Number of rounds in free magazine.
|
/// Number of rounds in free magazine.
|
||||||
free_rounds: usize,
|
free_rounds: u8,
|
||||||
/// Local per-CPU depot.
|
/// Local per-CPU depot.
|
||||||
depot: Depot,
|
depot: Depot,
|
||||||
};
|
};
|
||||||
@@ -491,11 +480,10 @@ inline fn wma_mix(old: usize, new: usize) usize {
|
|||||||
return (3 * old + new * wma_unit) / 4;
|
return (3 * old + new * wma_unit) / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cpus_alloc() []rtl.CachePaddedType(Cpu) {
|
fn cpus_alloc() [*]rtl.CachePaddedType(Cpu) {
|
||||||
const ptr = cpu_zone.alloc(.{}) catch
|
const ptr = cpu_zone.alloc(.{}) catch
|
||||||
@panic("failed to allocate zone CPU state");
|
@panic("failed to allocate zone CPU state");
|
||||||
const raw: [*]rtl.CachePaddedType(Cpu) = @ptrCast(@alignCast(ptr));
|
return @ptrCast(@alignCast(ptr));
|
||||||
return raw[0..ke.ncpus];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Zone = struct {
|
pub const Zone = struct {
|
||||||
@@ -511,8 +499,6 @@ pub const Zone = struct {
|
|||||||
chunk_size: usize,
|
chunk_size: usize,
|
||||||
/// Zone lock.
|
/// Zone lock.
|
||||||
lock: ke.Mutex,
|
lock: ke.Mutex,
|
||||||
/// List of full slabs (fully allocated) in this zone.
|
|
||||||
full_slabs: rtl.List,
|
|
||||||
/// List of partial slabs (partially allocated) in this zone.
|
/// List of partial slabs (partially allocated) in this zone.
|
||||||
partial_slabs: rtl.List,
|
partial_slabs: rtl.List,
|
||||||
/// List of free slabs (fully unallocated) in this zone.
|
/// List of free slabs (fully unallocated) in this zone.
|
||||||
@@ -525,21 +511,22 @@ pub const Zone = struct {
|
|||||||
depot_contention_wma: usize,
|
depot_contention_wma: usize,
|
||||||
depot_contention_cur: usize,
|
depot_contention_cur: usize,
|
||||||
/// Size of the per-CPU depot.
|
/// Size of the per-CPU depot.
|
||||||
cpu_depot_size: usize,
|
cpu_depot_size: u32,
|
||||||
/// Maximum size of the per-CPU depot.
|
/// Maximum size of the per-CPU depot.
|
||||||
cpu_depot_limit: usize,
|
cpu_depot_limit: u32,
|
||||||
trim_depot: bool,
|
|
||||||
/// Don't assume that the memory we're managing is actually memory.
|
|
||||||
dont_touch: bool,
|
|
||||||
|
|
||||||
/// Per-CPU state.
|
/// Per-CPU state.
|
||||||
cpus: []rtl.CachePaddedType(Cpu),
|
cpus: [*]rtl.CachePaddedType(Cpu),
|
||||||
|
/// Various boolean flags (packed)
|
||||||
|
flags: Flags,
|
||||||
|
|
||||||
|
/// Weighted moving average of `depot.empty_mags.min`, scaled by `wma_unit`.
|
||||||
|
empty_mags_wma: usize,
|
||||||
|
/// Weighted moving average of `depot.full_mags.min`, scaled by `wma_unit`.
|
||||||
|
full_mags_wma: usize,
|
||||||
|
|
||||||
ctor: ?*const fn (obj: *anyopaque) void,
|
ctor: ?*const fn (obj: *anyopaque) void,
|
||||||
dtor: ?*const fn (obj: *anyopaque) void,
|
dtor: ?*const fn (obj: *anyopaque) void,
|
||||||
|
|
||||||
use_magazines: bool,
|
|
||||||
|
|
||||||
reuse_policy: ReusePolicy,
|
reuse_policy: ReusePolicy,
|
||||||
|
|
||||||
smr: ?*ke.smr.Domain,
|
smr: ?*ke.smr.Domain,
|
||||||
@@ -584,6 +571,15 @@ pub const Zone = struct {
|
|||||||
arg: ?*anyopaque = null,
|
arg: ?*anyopaque = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Flags = packed struct {
|
||||||
|
/// Don't assume that the memory we're managing is actually memory.
|
||||||
|
dont_touch: bool,
|
||||||
|
/// Whether magazines are enabled for this zone.
|
||||||
|
use_magazines: bool,
|
||||||
|
/// Internal flag set when the depot needs to be trimmed.
|
||||||
|
trim_depot: bool,
|
||||||
|
};
|
||||||
|
|
||||||
/// Initialize a zone.
|
/// Initialize a zone.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
self: *Zone,
|
self: *Zone,
|
||||||
@@ -599,7 +595,6 @@ pub const Zone = struct {
|
|||||||
else
|
else
|
||||||
Slab.calc_slab_size(chunk_size);
|
Slab.calc_slab_size(chunk_size);
|
||||||
|
|
||||||
self.full_slabs.init();
|
|
||||||
self.partial_slabs.init();
|
self.partial_slabs.init();
|
||||||
self.free_slabs.init();
|
self.free_slabs.init();
|
||||||
|
|
||||||
@@ -612,16 +607,18 @@ pub const Zone = struct {
|
|||||||
self.dtor = options.dtor;
|
self.dtor = options.dtor;
|
||||||
self.lock = ke.Mutex.init("zone");
|
self.lock = ke.Mutex.init("zone");
|
||||||
self.depot_lock = ke.Mutex.init("zone_depot");
|
self.depot_lock = ke.Mutex.init("zone_depot");
|
||||||
self.use_magazines = options.magazines;
|
self.flags.use_magazines = options.magazines;
|
||||||
|
|
||||||
self.depot = std.mem.zeroes(Depot);
|
self.depot = std.mem.zeroes(Depot);
|
||||||
self.depot_contention_cur = 0;
|
self.depot_contention_cur = 0;
|
||||||
self.depot_contention_wma = 0;
|
self.depot_contention_wma = 0;
|
||||||
self.cpu_depot_size = 0;
|
self.cpu_depot_size = 0;
|
||||||
self.cpu_depot_limit = max_local_memory.load() /
|
self.cpu_depot_limit = @intCast(max_local_memory.load() /
|
||||||
(self.chunk_size * magazine_size.load());
|
(self.chunk_size * magazine_size.load()));
|
||||||
self.trim_depot = false;
|
self.flags.trim_depot = false;
|
||||||
self.dont_touch = options.import != null;
|
self.flags.dont_touch = options.import != null;
|
||||||
|
self.empty_mags_wma = 0;
|
||||||
|
self.full_mags_wma = 0;
|
||||||
|
|
||||||
// SMR zones have to be FIFO so that the minimum sequence can be
|
// SMR zones have to be FIFO so that the minimum sequence can be
|
||||||
// obtained by checking the list head.
|
// obtained by checking the list head.
|
||||||
@@ -671,7 +668,7 @@ pub const Zone = struct {
|
|||||||
/// Allocate an object from the zone.
|
/// Allocate an object from the zone.
|
||||||
pub fn alloc(self: *Zone, opts: AllocOpts) mm.Error!*anyopaque {
|
pub fn alloc(self: *Zone, opts: AllocOpts) mm.Error!*anyopaque {
|
||||||
// Try grabbing an object from the magazine layer.
|
// Try grabbing an object from the magazine layer.
|
||||||
if (magazines_initialized and self.use_magazines) {
|
if (magazines_initialized and self.flags.use_magazines) {
|
||||||
@branchHint(.likely);
|
@branchHint(.likely);
|
||||||
|
|
||||||
var buf: *anyopaque = undefined;
|
var buf: *anyopaque = undefined;
|
||||||
@@ -685,7 +682,7 @@ pub const Zone = struct {
|
|||||||
cpu.alloc_rounds -= 1;
|
cpu.alloc_rounds -= 1;
|
||||||
buf = alloc_mag.rounds_ptr()[cpu.alloc_rounds];
|
buf = alloc_mag.rounds_ptr()[cpu.alloc_rounds];
|
||||||
|
|
||||||
if (is_poison_enabled and !self.dont_touch) {
|
if (is_poison_enabled and !self.flags.dont_touch) {
|
||||||
if (!check_poison(buf, self.obj_size, free_poison)) {
|
if (!check_poison(buf, self.obj_size, free_poison)) {
|
||||||
@panic("poison mismatch: possible use-after-free");
|
@panic("poison mismatch: possible use-after-free");
|
||||||
}
|
}
|
||||||
@@ -708,7 +705,7 @@ pub const Zone = struct {
|
|||||||
// swap them. The free magazine now becomes our alloc
|
// swap them. The free magazine now becomes our alloc
|
||||||
// magazine.
|
// magazine.
|
||||||
std.mem.swap(?*Magazine, &cpu.alloc, &cpu.free);
|
std.mem.swap(?*Magazine, &cpu.alloc, &cpu.free);
|
||||||
std.mem.swap(usize, &cpu.alloc_rounds, &cpu.free_rounds);
|
std.mem.swap(u8, &cpu.alloc_rounds, &cpu.free_rounds);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,7 +733,7 @@ pub const Zone = struct {
|
|||||||
// Local depot is empty, rebalance with the zone depot.
|
// Local depot is empty, rebalance with the zone depot.
|
||||||
// Purposefully race against cpu_depot_size because locking is
|
// Purposefully race against cpu_depot_size because locking is
|
||||||
// tricky here, we may get a stale value but it is harmless.
|
// tricky here, we may get a stale value but it is harmless.
|
||||||
const n = rtl.mem.racy_read(usize, &self.cpu_depot_size);
|
const n = rtl.mem.racy_read(u32, &self.cpu_depot_size);
|
||||||
|
|
||||||
self.alloc_depot_rebalance(n, cpu);
|
self.alloc_depot_rebalance(n, cpu);
|
||||||
|
|
||||||
@@ -793,7 +790,7 @@ pub const Zone = struct {
|
|||||||
|
|
||||||
if (cpu.alloc == null) {
|
if (cpu.alloc == null) {
|
||||||
cpu.alloc = nm;
|
cpu.alloc = nm;
|
||||||
cpu.alloc_rounds = cnt;
|
cpu.alloc_rounds = @intCast(cnt);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// We lost the race. If the magazine we got was fully
|
// We lost the race. If the magazine we got was fully
|
||||||
@@ -852,7 +849,7 @@ pub const Zone = struct {
|
|||||||
|
|
||||||
/// Free an object back to the zone.
|
/// Free an object back to the zone.
|
||||||
pub fn free(self: *Zone, obj: *anyopaque) void {
|
pub fn free(self: *Zone, obj: *anyopaque) void {
|
||||||
if (magazines_initialized and self.use_magazines) {
|
if (magazines_initialized and self.flags.use_magazines) {
|
||||||
@branchHint(.likely);
|
@branchHint(.likely);
|
||||||
|
|
||||||
var cpu = &self.cpus[ke.cpu.current()].value;
|
var cpu = &self.cpus[ke.cpu.current()].value;
|
||||||
@@ -869,7 +866,7 @@ pub const Zone = struct {
|
|||||||
// the object.
|
// the object.
|
||||||
const free_mag = cpu.free orelse unreachable;
|
const free_mag = cpu.free orelse unreachable;
|
||||||
if (is_poison_enabled and self.smr == null and
|
if (is_poison_enabled and self.smr == null and
|
||||||
!self.dont_touch)
|
!self.flags.dont_touch)
|
||||||
{
|
{
|
||||||
if (self.dtor) |dtor| {
|
if (self.dtor) |dtor| {
|
||||||
dtor(obj);
|
dtor(obj);
|
||||||
@@ -901,7 +898,7 @@ pub const Zone = struct {
|
|||||||
// If the alloc magazine is completely empty, swap them.
|
// If the alloc magazine is completely empty, swap them.
|
||||||
// The empty alloc magazine becomes our new free magazine.
|
// The empty alloc magazine becomes our new free magazine.
|
||||||
std.mem.swap(?*Magazine, &cpu.alloc, &cpu.free);
|
std.mem.swap(?*Magazine, &cpu.alloc, &cpu.free);
|
||||||
std.mem.swap(usize, &cpu.alloc_rounds, &cpu.free_rounds);
|
std.mem.swap(u8, &cpu.alloc_rounds, &cpu.free_rounds);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -924,7 +921,7 @@ pub const Zone = struct {
|
|||||||
|
|
||||||
// Local depot is empty, rebalance with the zone depot.
|
// Local depot is empty, rebalance with the zone depot.
|
||||||
// Race explained in alloc.
|
// Race explained in alloc.
|
||||||
const n = rtl.mem.racy_read(usize, &self.cpu_depot_size);
|
const n = rtl.mem.racy_read(u32, &self.cpu_depot_size);
|
||||||
|
|
||||||
self.free_depot_rebalance(n, cpu);
|
self.free_depot_rebalance(n, cpu);
|
||||||
|
|
||||||
@@ -986,13 +983,13 @@ pub const Zone = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn slab_free(self: *Zone, obj: *anyopaque, poison: bool) void {
|
fn slab_free(self: *Zone, obj: *anyopaque, poison: bool) void {
|
||||||
if (!is_poison_enabled or poison and !self.dont_touch) {
|
if (!is_poison_enabled or poison and !self.flags.dont_touch) {
|
||||||
if (self.dtor) |dtor| {
|
if (self.dtor) |dtor| {
|
||||||
dtor(obj);
|
dtor(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_poison_enabled and poison and !self.dont_touch) {
|
if (is_poison_enabled and poison and !self.flags.dont_touch) {
|
||||||
fill_with_poison(obj, self.obj_size, free_poison);
|
fill_with_poison(obj, self.obj_size, free_poison);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1021,7 +1018,6 @@ pub const Zone = struct {
|
|||||||
if (was_full) {
|
if (was_full) {
|
||||||
// There were no buffers in the slab, so it wasn't in the freelist.
|
// There were no buffers in the slab, so it wasn't in the freelist.
|
||||||
// Now that there is a buffer, add it back to the freelist.
|
// Now that there is a buffer, add it back to the freelist.
|
||||||
slab.link.remove();
|
|
||||||
self.partial_slabs.insert_tail(&slab.link);
|
self.partial_slabs.insert_tail(&slab.link);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1082,7 +1078,6 @@ pub const Zone = struct {
|
|||||||
|
|
||||||
if (slab.refcount == slab.capacity) {
|
if (slab.refcount == slab.capacity) {
|
||||||
slab.link.remove();
|
slab.link.remove();
|
||||||
self.full_slabs.insert_tail(&slab.link);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_poison_enabled) {
|
if (is_poison_enabled) {
|
||||||
@@ -1146,14 +1141,14 @@ pub const Zone = struct {
|
|||||||
// interval, then the working set size is 10 and we can
|
// interval, then the working set size is 10 and we can
|
||||||
// reclaim 37 magazines. We keep track of this in a weighted moving
|
// reclaim 37 magazines. We keep track of this in a weighted moving
|
||||||
// average (WMA) to bias recent utilization when trimming.
|
// average (WMA) to bias recent utilization when trimming.
|
||||||
self.depot.empty_mags.wma = wma_mix(
|
self.empty_mags_wma = wma_mix(
|
||||||
self.depot.empty_mags.wma,
|
self.empty_mags_wma,
|
||||||
self.depot.empty_mags.min,
|
self.depot.empty_mags.min,
|
||||||
);
|
);
|
||||||
self.depot.empty_mags.min = self.depot.empty_mags.num;
|
self.depot.empty_mags.min = self.depot.empty_mags.num;
|
||||||
|
|
||||||
self.depot.full_mags.wma = wma_mix(
|
self.full_mags_wma = wma_mix(
|
||||||
self.depot.full_mags.wma,
|
self.full_mags_wma,
|
||||||
self.depot.full_mags.min,
|
self.depot.full_mags.min,
|
||||||
);
|
);
|
||||||
self.depot.full_mags.min = self.depot.full_mags.num;
|
self.depot.full_mags.min = self.depot.full_mags.num;
|
||||||
@@ -1163,10 +1158,10 @@ pub const Zone = struct {
|
|||||||
var cur = self.depot_contention_cur * wma_unit /
|
var cur = self.depot_contention_cur * wma_unit /
|
||||||
(ke.ncpus * update_interval_s);
|
(ke.ncpus * update_interval_s);
|
||||||
|
|
||||||
// WMA formula.
|
// EWMA formula.
|
||||||
cur = (3 * old + cur) / 4;
|
cur = (3 * old + cur) / 4;
|
||||||
|
|
||||||
if (self.use_magazines) {
|
if (self.flags.use_magazines) {
|
||||||
if (self.cpu_depot_size < self.cpu_depot_limit and
|
if (self.cpu_depot_size < self.cpu_depot_limit and
|
||||||
cur > depot_grow_level.load())
|
cur > depot_grow_level.load())
|
||||||
{
|
{
|
||||||
@@ -1176,31 +1171,34 @@ pub const Zone = struct {
|
|||||||
// good or not.
|
// good or not.
|
||||||
cur = (depot_grow_level.load() + depot_shrink_level.load()) / 2;
|
cur = (depot_grow_level.load() + depot_shrink_level.load()) / 2;
|
||||||
|
|
||||||
const size = if (self.cpu_depot_size == 0)
|
const size: usize = if (self.cpu_depot_size == 0)
|
||||||
2
|
2
|
||||||
else
|
else
|
||||||
// Grow by 1.5x.
|
// Grow by 1.5x.
|
||||||
(3 * self.cpu_depot_size) / 2;
|
(3 * @as(usize, self.cpu_depot_size)) / 2;
|
||||||
|
|
||||||
// Clamp it.
|
// Clamp it.
|
||||||
self.cpu_depot_size = @min(size, self.cpu_depot_limit);
|
self.cpu_depot_size = @intCast(@min(
|
||||||
|
size,
|
||||||
|
@as(usize, self.cpu_depot_limit),
|
||||||
|
));
|
||||||
} else if (self.cpu_depot_size > 0 and
|
} else if (self.cpu_depot_size > 0 and
|
||||||
cur <= depot_shrink_level.load())
|
cur <= depot_shrink_level.load())
|
||||||
{
|
{
|
||||||
// We should shrink the depot.
|
// We should shrink the depot.
|
||||||
cur = (depot_grow_level.load() + depot_shrink_level.load()) / 2;
|
cur = (depot_grow_level.load() + depot_shrink_level.load()) / 2;
|
||||||
self.cpu_depot_size -= 1;
|
self.cpu_depot_size -= 1;
|
||||||
self.trim_depot = true;
|
self.flags.trim_depot = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.depot_contention_cur = 0;
|
self.depot_contention_cur = 0;
|
||||||
self.depot_contention_wma = cur;
|
self.depot_contention_wma = cur;
|
||||||
|
|
||||||
const trim_depot = self.trim_depot;
|
const trim_depot = self.flags.trim_depot;
|
||||||
const should_trim = self.trim_needed();
|
const should_trim = self.trim_needed();
|
||||||
|
|
||||||
self.trim_depot = false;
|
self.flags.trim_depot = false;
|
||||||
|
|
||||||
self.depot_lock.release();
|
self.depot_lock.release();
|
||||||
|
|
||||||
@@ -1211,12 +1209,12 @@ pub const Zone = struct {
|
|||||||
|
|
||||||
/// Returns whether or not we should trim excess magazines.
|
/// Returns whether or not we should trim excess magazines.
|
||||||
fn trim_needed(self: *Zone) bool {
|
fn trim_needed(self: *Zone) bool {
|
||||||
if (!self.use_magazines) return false;
|
if (!self.flags.use_magazines) return false;
|
||||||
if (self.trim_depot) return true;
|
if (self.flags.trim_depot) return true;
|
||||||
|
|
||||||
const empty = @min(
|
const empty = @min(
|
||||||
self.depot.empty_mags.wma,
|
self.empty_mags_wma,
|
||||||
self.depot.empty_mags.min * wma_unit,
|
@as(usize, self.depot.empty_mags.min) * wma_unit,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (empty > excess_magazines.load() * wma_unit) {
|
if (empty > excess_magazines.load() * wma_unit) {
|
||||||
@@ -1225,8 +1223,8 @@ pub const Zone = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const full = @min(
|
const full = @min(
|
||||||
self.depot.full_mags.wma,
|
self.full_mags_wma,
|
||||||
self.depot.full_mags.min * wma_unit,
|
@as(usize, self.depot.full_mags.min) * wma_unit,
|
||||||
);
|
);
|
||||||
|
|
||||||
const full_bytes = full * magazine_size.load() * self.chunk_size;
|
const full_bytes = full * magazine_size.load() * self.chunk_size;
|
||||||
@@ -1249,8 +1247,14 @@ pub const Zone = struct {
|
|||||||
// unlocked.
|
// unlocked.
|
||||||
self.lock.acquire();
|
self.lock.acquire();
|
||||||
|
|
||||||
const empty_mags = self.trim_maglist(&self.depot.empty_mags);
|
const empty_mags = self.trim_maglist(
|
||||||
const full_mags = self.trim_maglist(&self.depot.full_mags);
|
&self.depot.empty_mags,
|
||||||
|
&self.empty_mags_wma,
|
||||||
|
);
|
||||||
|
const full_mags = self.trim_maglist(
|
||||||
|
&self.depot.full_mags,
|
||||||
|
&self.full_mags_wma,
|
||||||
|
);
|
||||||
var depot = std.mem.zeroes(Depot);
|
var depot = std.mem.zeroes(Depot);
|
||||||
|
|
||||||
if (trim_depot) {
|
if (trim_depot) {
|
||||||
@@ -1287,7 +1291,7 @@ pub const Zone = struct {
|
|||||||
/// Called when memory is low and we need to make memory ASAP.
|
/// Called when memory is low and we need to make memory ASAP.
|
||||||
/// Drains all magazines from the depot.
|
/// Drains all magazines from the depot.
|
||||||
fn drain(self: *Zone) void {
|
fn drain(self: *Zone) void {
|
||||||
if (!self.use_magazines) return;
|
if (!self.flags.use_magazines) return;
|
||||||
|
|
||||||
// Detach magazines before invoking release callbacks.
|
// Detach magazines before invoking release callbacks.
|
||||||
self.lock.acquire();
|
self.lock.acquire();
|
||||||
@@ -1297,6 +1301,8 @@ pub const Zone = struct {
|
|||||||
// Take all elements from the zone depot.
|
// Take all elements from the zone depot.
|
||||||
var depot = self.depot;
|
var depot = self.depot;
|
||||||
self.depot = std.mem.zeroes(Depot);
|
self.depot = std.mem.zeroes(Depot);
|
||||||
|
self.empty_mags_wma = 0;
|
||||||
|
self.full_mags_wma = 0;
|
||||||
|
|
||||||
self.depot_lock.release();
|
self.depot_lock.release();
|
||||||
|
|
||||||
@@ -1328,18 +1334,22 @@ pub const Zone = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Detach excess magazines for destruction.
|
/// Detach excess magazines for destruction.
|
||||||
fn trim_maglist(self: *Zone, maglist: *MagazineList) ?*Magazine {
|
fn trim_maglist(
|
||||||
|
self: *Zone,
|
||||||
|
maglist: *MagazineList,
|
||||||
|
wma: *usize,
|
||||||
|
) ?*Magazine {
|
||||||
self.depot_lock.acquire();
|
self.depot_lock.acquire();
|
||||||
|
|
||||||
// Grab the magazines and WSS under the depot lock.
|
// Grab the magazines and WSS under the depot lock.
|
||||||
const target = @min(
|
const target = @min(
|
||||||
maglist.min * wma_unit,
|
@as(usize, maglist.min) * wma_unit,
|
||||||
maglist.wma,
|
wma.*,
|
||||||
) / wma_unit;
|
) / wma_unit;
|
||||||
|
|
||||||
var removed_head: ?*Magazine = maglist.head;
|
var removed_head: ?*Magazine = maglist.head;
|
||||||
var last_removed: ?*Magazine = null;
|
var last_removed: ?*Magazine = null;
|
||||||
var removed: usize = 0;
|
var removed: u32 = 0;
|
||||||
|
|
||||||
for (0..target) |_| {
|
for (0..target) |_| {
|
||||||
if (maglist.head) |head| {
|
if (maglist.head) |head| {
|
||||||
@@ -1361,7 +1371,7 @@ pub const Zone = struct {
|
|||||||
|
|
||||||
maglist.min -= removed;
|
maglist.min -= removed;
|
||||||
maglist.num -= removed;
|
maglist.num -= removed;
|
||||||
maglist.wma -= removed * wma_unit;
|
wma.* -= @as(usize, removed) * wma_unit;
|
||||||
|
|
||||||
self.depot_lock.release();
|
self.depot_lock.release();
|
||||||
|
|
||||||
@@ -1790,7 +1800,7 @@ pub fn early_init() linksection(r.init) void {
|
|||||||
zone_list_lock = ke.Mutex.init("zone_list");
|
zone_list_lock = ke.Mutex.init("zone_list");
|
||||||
|
|
||||||
for (0..generic_zones.len, &generic_zones) |i, *zone| {
|
for (0..generic_zones.len, &generic_zones) |i, *zone| {
|
||||||
zone.init(generic_zone_names[i], @as(usize, 1) << @intCast(i + 3), .{});
|
zone.init(generic_names[i], generic_sizes[i], .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
magazine_zone.init(
|
magazine_zone.init(
|
||||||
@@ -1815,7 +1825,7 @@ pub fn late_init() linksection(r.init) void {
|
|||||||
var zone = all_zones;
|
var zone = all_zones;
|
||||||
|
|
||||||
while (zone) |z| {
|
while (zone) |z| {
|
||||||
if (z.use_magazines) {
|
if (z.flags.use_magazines) {
|
||||||
z.cpus = cpus_alloc();
|
z.cpus = cpus_alloc();
|
||||||
|
|
||||||
for (0..ke.ncpus) |i| {
|
for (0..ke.ncpus) |i| {
|
||||||
@@ -1949,11 +1959,58 @@ fn gpa_free(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn zone_for(size: usize) ?*Zone {
|
fn zone_for(size: usize) ?*Zone {
|
||||||
for (&generic_zones) |*zone| {
|
if (size > generic_max_size) return null;
|
||||||
if (zone.obj_size >= size) {
|
return &generic_zones[size_to_bucket(size)];
|
||||||
return zone;
|
}
|
||||||
}
|
|
||||||
|
/// Linear-log bucketing is used for sizing the generic buckets, see
|
||||||
|
/// https://pvk.ca/Blog/2015/06/27/linear-log-bucketing-fast-versatile-simple.
|
||||||
|
/// These formulas come from jemalloc.
|
||||||
|
const N = generic_zones_num;
|
||||||
|
|
||||||
|
fn bucket_to_size(index: usize) usize {
|
||||||
|
if (index == 0) return 8;
|
||||||
|
|
||||||
|
const i = index - 1;
|
||||||
|
const group = i / 4;
|
||||||
|
const offset = i % 4;
|
||||||
|
|
||||||
|
if (group == 0) {
|
||||||
|
return (offset + 1) * 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return (offset + 5) << (group + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_to_bucket(size: usize) usize {
|
||||||
|
if (size <= 8) return 0;
|
||||||
|
if (size <= 64) return ((size - 1) / 16) + 1;
|
||||||
|
|
||||||
|
const lg: usize = std.math.log2_int(usize, size - 1);
|
||||||
|
const group = lg - 5;
|
||||||
|
const shift: std.math.Log2Int(usize) = @intCast(lg - 2);
|
||||||
|
const offset = ((size - 1) >> shift) - 4;
|
||||||
|
return 1 + (group * 4) + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_generic_sizes() [N]usize {
|
||||||
|
var arr: [N]usize = undefined;
|
||||||
|
|
||||||
|
for (0..N) |i| {
|
||||||
|
arr[i] = comptime bucket_to_size(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_generic_names(
|
||||||
|
comptime array: [N]usize,
|
||||||
|
) [N][]const u8 {
|
||||||
|
var ret: [N][]const u8 = undefined;
|
||||||
|
|
||||||
|
for (0..N) |i| {
|
||||||
|
ret[i] = std.fmt.comptimePrint("zone-{}", .{array[i]});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user