authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-07-05 13:34:14-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-07-07 22:59:52-04:00
log383cffbfae2d6be5862cbadaf138618c5b37b345
tree91f26367041a8bbea33989325026bb2d80a71d03
parent92ddb959a7c8877c98363b27c71cd5ae4b9603f4

InternPool: temporarily disable multi-threaded behavior

This reduces the cost of the new data structure until the multi-threaded behavior is actually used.

3 files changed, 67 insertions(+), 41 deletions(-)

lib/std/Thread/Pool.zig+8-3
......@@ -8,8 +8,13 @@ cond: std.Thread.Condition = .{},
88run_queue: RunQueue = .{},
99is_running: bool = true,
1010allocator: std.mem.Allocator,
11threads: []std.Thread,
12ids: std.AutoArrayHashMapUnmanaged(std.Thread.Id, void),
11threads: if (builtin.single_threaded) [0]std.Thread else []std.Thread,
12ids: if (builtin.single_threaded) struct {
13 inline fn deinit(_: @This(), _: std.mem.Allocator) void {}
14 fn getIndex(_: @This(), _: std.Thread.Id) usize {
15 return 0;
16 }
17} else std.AutoArrayHashMapUnmanaged(std.Thread.Id, void),
1318
1419const RunQueue = std.SinglyLinkedList(Runnable);
1520const Runnable = struct {
......@@ -29,7 +34,7 @@ pub fn init(pool: *Pool, options: Options) !void {
2934
3035 pool.* = .{
3136 .allocator = allocator,
32 .threads = &[_]std.Thread{},
37 .threads = if (builtin.single_threaded) .{} else &.{},
3338 .ids = .{},
3439 };
3540
src/InternPool.zig+58-37
......@@ -4,11 +4,10 @@
44
55locals: []Local = &.{},
66shards: []Shard = &.{},
7tid_width: std.math.Log2Int(u32) = 0,
8tid_shift_31: std.math.Log2Int(u32) = 31,
9tid_shift_32: std.math.Log2Int(u32) = 31,
7tid_width: if (single_threaded) u0 else std.math.Log2Int(u32) = 0,
8tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32) = if (single_threaded) 0 else 31,
9tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32) = if (single_threaded) 0 else 31,
1010
11//items: std.MultiArrayList(Item) = .{},
1211extra: std.ArrayListUnmanaged(u32) = .{},
1312/// On 32-bit systems, this array is ignored and extra is used for everything.
1413/// On 64-bit systems, this array is used for big integers and associated metadata.
......@@ -92,6 +91,14 @@ free_dep_entries: std.ArrayListUnmanaged(DepEntry.Index) = .{},
9291/// Value is the `Decl` of the struct that represents this `File`.
9392files: std.AutoArrayHashMapUnmanaged(Cache.BinDigest, OptionalDeclIndex) = .{},
9493
94/// Whether a multi-threaded intern pool is useful.
95/// Currently `false` until the intern pool is actually accessed
96/// from multiple threads to reduce the cost of this data structure.
97const want_multi_threaded = false;
98
99/// Whether a single-threaded intern pool impl is in use.
100pub const single_threaded = builtin.single_threaded or !want_multi_threaded;
101
95102pub const FileIndex = enum(u32) {
96103 _,
97104};
......@@ -497,19 +504,23 @@ const Local = struct {
497504 var new_list: ListSelf = .{ .bytes = @ptrCast(buf[bytes_offset..].ptr) };
498505 new_list.header().* = .{ .capacity = capacity };
499506 const len = mutable.lenPtr().*;
500 const old_slice = mutable.list.view().slice();
501 const new_slice = new_list.view().slice();
502 inline for (fields) |field| {
503 @memcpy(new_slice.items(field)[0..len], old_slice.items(field)[0..len]);
507 // this cold, quickly predictable, condition enables
508 // the `MultiArrayList` optimization in `view`
509 if (len > 0) {
510 const old_slice = mutable.list.view().slice();
511 const new_slice = new_list.view().slice();
512 inline for (fields) |field| @memcpy(new_slice.items(field)[0..len], old_slice.items(field)[0..len]);
504513 }
505514 mutable.list.release(new_list);
506515 }
507516
508517 fn view(mutable: Mutable) View {
518 const capacity = mutable.capacityPtr().*;
519 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`
509520 return .{
510521 .bytes = mutable.list.bytes,
511522 .len = mutable.lenPtr().*,
512 .capacity = mutable.capacityPtr().*,
523 .capacity = capacity,
513524 };
514525 }
515526
......@@ -550,6 +561,7 @@ const Local = struct {
550561
551562 fn view(list: ListSelf) View {
552563 const capacity = list.header().capacity;
564 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`
553565 return .{
554566 .bytes = list.bytes,
555567 .len = capacity,
......@@ -665,13 +677,8 @@ const Shard = struct {
665677 }
666678};
667679
668fn getShard(ip: *InternPool, tid: Zcu.PerThread.Id) *Shard {
669 return &ip.shards[@intFromEnum(tid)];
670}
671
672680fn getTidMask(ip: *const InternPool) u32 {
673 assert(std.math.isPowerOfTwo(ip.shards.len));
674 return @intCast(ip.shards.len - 1);
681 return (@as(u32, 1) << ip.tid_width) - 1;
675682}
676683
677684fn getIndexMask(ip: *const InternPool, comptime BackingInt: type) u32 {
......@@ -809,7 +816,7 @@ pub const String = enum(u32) {
809816 };
810817 }
811818
812 fn toOverlongSlice(string: String, ip: *const InternPool) []const u8 {
819 noinline fn toOverlongSlice(string: String, ip: *const InternPool) []const u8 {
813820 const unwrapped = string.unwrap(ip);
814821 return ip.getLocalShared(unwrapped.tid).strings.acquire().view().items(.@"0")[unwrapped.index..];
815822 }
......@@ -3230,19 +3237,35 @@ pub const Index = enum(u32) {
32303237 }
32313238 };
32323239
3233 pub fn getItem(index: Index, ip: *const InternPool) Item {
3234 const unwrapped = index.unwrap(ip);
3235 return ip.getLocalShared(unwrapped.tid).items.acquire().view().get(unwrapped.index);
3240 pub inline fn getItem(index: Index, ip: *const InternPool) Item {
3241 const item_ptr = index.itemPtr(ip);
3242 const tag = @atomicLoad(Tag, item_ptr.tag_ptr, .acquire);
3243 return .{ .tag = tag, .data = item_ptr.data_ptr.* };
32363244 }
32373245
3238 pub fn getTag(index: Index, ip: *const InternPool) Tag {
3239 const unwrapped = index.unwrap(ip);
3240 return ip.getLocalShared(unwrapped.tid).items.acquire().view().items(.tag)[unwrapped.index];
3246 pub inline fn getTag(index: Index, ip: *const InternPool) Tag {
3247 const item_ptr = index.itemPtr(ip);
3248 return @atomicLoad(Tag, item_ptr.tag_ptr, .acquire);
32413249 }
32423250
3243 pub fn getData(index: Index, ip: *const InternPool) u32 {
3244 const unwrapped = index.unwrap(ip);
3245 return ip.getLocalShared(unwrapped.tid).items.acquire().view().items(.data)[unwrapped.index];
3251 pub inline fn getData(index: Index, ip: *const InternPool) u32 {
3252 return index.getItem(ip).data;
3253 }
3254
3255 const ItemPtr = struct {
3256 tag_ptr: *Tag,
3257 data_ptr: *u32,
3258 };
3259 fn itemPtr(index: Index, ip: *const InternPool) ItemPtr {
3260 const unwrapped: Unwrapped = if (single_threaded) .{
3261 .tid = .main,
3262 .index = @intFromEnum(index),
3263 } else index.unwrap(ip);
3264 const slice = ip.getLocalShared(unwrapped.tid).items.acquire().view().slice();
3265 return .{
3266 .tag_ptr = &slice.items(.tag)[unwrapped.index],
3267 .data_ptr = &slice.items(.data)[unwrapped.index],
3268 };
32463269 }
32473270
32483271 const Unwrapped = struct {
......@@ -4905,11 +4928,12 @@ pub const MemoizedCall = struct {
49054928 result: Index,
49064929};
49074930
4908pub fn init(ip: *InternPool, gpa: Allocator, total_threads: usize) !void {
4931pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
49094932 errdefer ip.deinit(gpa);
49104933 assert(ip.locals.len == 0 and ip.shards.len == 0);
49114934
4912 ip.locals = try gpa.alloc(Local, total_threads);
4935 const used_threads = if (single_threaded) 1 else available_threads;
4936 ip.locals = try gpa.alloc(Local, used_threads);
49134937 @memset(ip.locals, .{
49144938 .shared = .{
49154939 .items = Local.List(Item).empty,
......@@ -4922,9 +4946,9 @@ pub fn init(ip: *InternPool, gpa: Allocator, total_threads: usize) !void {
49224946 },
49234947 });
49244948
4925 ip.tid_width = @intCast(std.math.log2_int_ceil(usize, total_threads));
4926 ip.tid_shift_31 = 31 - ip.tid_width;
4927 ip.tid_shift_32 = ip.tid_shift_31 +| 1;
4949 ip.tid_width = @intCast(std.math.log2_int_ceil(usize, used_threads));
4950 ip.tid_shift_31 = if (single_threaded) 0 else 31 - ip.tid_width;
4951 ip.tid_shift_32 = if (single_threaded) 0 else ip.tid_shift_31 +| 1;
49284952 ip.shards = try gpa.alloc(Shard, @as(usize, 1) << ip.tid_width);
49294953 @memset(ip.shards, .{
49304954 .shared = .{
......@@ -7063,7 +7087,7 @@ pub fn getExternFunc(
70637087 .tag = .extern_func,
70647088 .data = extra_index,
70657089 });
7066 errdefer ip.items.lenPtr().* -= 1;
7090 errdefer items.lenPtr().* -= 1;
70677091 return gop.put();
70687092}
70697093
......@@ -10146,12 +10170,9 @@ pub fn iesFuncIndex(ip: *const InternPool, ies_index: Index) Index {
1014610170/// error set function. The returned pointer is invalidated when anything is
1014710171/// added to `ip`.
1014810172pub fn iesResolved(ip: *const InternPool, ies_index: Index) *Index {
10149 assert(ies_index != .none);
10150 const tags = ip.items.items(.tag);
10151 const datas = ip.items.items(.data);
10152 assert(tags[@intFromEnum(ies_index)] == .type_inferred_error_set);
10153 const func_index = datas[@intFromEnum(ies_index)];
10154 return funcIesResolved(ip, func_index);
10173 const ies_item = ies_index.getItem(ip);
10174 assert(ies_item.tag == .type_inferred_error_set);
10175 return funcIesResolved(ip, ies_item.data);
1015510176}
1015610177
1015710178/// Returns a mutable pointer to the resolved error set type of an inferred
src/Zcu/PerThread.zig+1-1
......@@ -3,7 +3,7 @@ zcu: *Zcu,
33/// Dense, per-thread unique index.
44tid: Id,
55
6pub const Id = if (builtin.single_threaded) enum { main } else enum(usize) { main, _ };
6pub const Id = if (InternPool.single_threaded) enum { main } else enum(usize) { main, _ };
77
88pub fn astGenFile(
99 pt: Zcu.PerThread,