From 40e222c43bc010a0f5bf0b83e431b5fab7f3b7ce Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 18 Mar 2025 21:02:01 -0700 Subject: [PATCH] Reduce binary size by 400 KB (#18280) Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> --- src/bit_set.zig | 8 +++ src/bun.js/api/bun/dns_resolver.zig | 87 +++++++++++++++++------------ src/bundler/bundle_v2.zig | 2 +- src/hive_array.zig | 34 ++++++----- src/http.zig | 8 ++- src/install/install.zig | 14 +++-- 6 files changed, 97 insertions(+), 56 deletions(-) diff --git a/src/bit_set.zig b/src/bit_set.zig index b313900f37..88c499df5f 100644 --- a/src/bit_set.zig +++ b/src/bit_set.zig @@ -190,6 +190,14 @@ pub fn IntegerBitSet(comptime size: u16) type { return @ctz(mask); } + /// Finds the index of the first unset bit. + /// If all bits are set, returns null. + pub fn findFirstUnset(self: Self) ?usize { + const mask = ~self.mask; + if (mask == 0) return null; + return @ctz(mask); + } + /// Finds the index of the first set bit, and unsets it. /// If no bits are set, returns null. pub fn toggleFirstSet(self: *Self) ?usize { diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index eb86300797..88f03afbd4 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -113,7 +113,7 @@ const LibInfo = struct { if (errno != 0) { request.head.promise.rejectTask(globalThis, globalThis.createErrorInstance("getaddrinfo_async_start error: {s}", .{@tagName(bun.C.getErrno(errno))})); - if (request.cache.pending_cache) this.pending_host_cache_native.available.set(request.cache.pos_in_pending); + if (request.cache.pending_cache) this.pending_host_cache_native.used.set(request.cache.pos_in_pending); this.vm.allocator.destroy(request); return promise_value; @@ -1146,10 +1146,7 @@ pub const GlobalData = struct { pub fn init(allocator: std.mem.Allocator, vm: *JSC.VirtualMachine) *GlobalData { const global = allocator.create(GlobalData) catch bun.outOfMemory(); global.* = .{ - .resolver = .{ - .vm = vm, - .polls = DNSResolver.PollsMap.init(allocator), - }, + .resolver = DNSResolver.setup(allocator, vm), }; return global; @@ -1781,22 +1778,22 @@ pub const DNSResolver = struct { .tag = .DNSResolver, }, - pending_host_cache_cares: PendingCache = .empty, - pending_host_cache_native: PendingCache = .empty, - pending_srv_cache_cares: SrvPendingCache = .empty, - pending_soa_cache_cares: SoaPendingCache = .empty, - pending_txt_cache_cares: TxtPendingCache = .empty, - pending_naptr_cache_cares: NaptrPendingCache = .empty, - pending_mx_cache_cares: MxPendingCache = .empty, - pending_caa_cache_cares: CaaPendingCache = .empty, - pending_ns_cache_cares: NSPendingCache = .empty, - pending_ptr_cache_cares: PtrPendingCache = .empty, - pending_cname_cache_cares: CnamePendingCache = .empty, - pending_a_cache_cares: APendingCache = .empty, - pending_aaaa_cache_cares: AAAAPendingCache = .empty, - pending_any_cache_cares: AnyPendingCache = .empty, - pending_addr_cache_cares: AddrPendingCache = .empty, - pending_nameinfo_cache_cares: NameInfoPendingCache = .empty, + pending_host_cache_cares: PendingCache, + pending_host_cache_native: PendingCache, + pending_srv_cache_cares: SrvPendingCache, + pending_soa_cache_cares: SoaPendingCache, + pending_txt_cache_cares: TxtPendingCache, + pending_naptr_cache_cares: NaptrPendingCache, + pending_mx_cache_cares: MxPendingCache, + pending_caa_cache_cares: CaaPendingCache, + pending_ns_cache_cares: NSPendingCache, + pending_ptr_cache_cares: PtrPendingCache, + pending_cname_cache_cares: CnamePendingCache, + pending_a_cache_cares: APendingCache, + pending_aaaa_cache_cares: AAAAPendingCache, + pending_any_cache_cares: AnyPendingCache, + pending_addr_cache_cares: AddrPendingCache, + pending_nameinfo_cache_cares: NameInfoPendingCache, pub usingnamespace JSC.Codegen.JSDNSResolver; pub usingnamespace bun.NewRefCounted(@This(), deinit, null); @@ -1820,12 +1817,32 @@ pub const DNSResolver = struct { pub usingnamespace bun.New(@This()); }; - pub fn init(allocator: std.mem.Allocator, vm: *JSC.VirtualMachine) *DNSResolver { - log("init", .{}); - return DNSResolver.new(.{ + pub fn setup(allocator: std.mem.Allocator, vm: *JSC.VirtualMachine) DNSResolver { + return .{ .vm = vm, .polls = DNSResolver.PollsMap.init(allocator), - }); + .pending_host_cache_cares = PendingCache.empty, + .pending_host_cache_native = PendingCache.empty, + .pending_srv_cache_cares = SrvPendingCache.empty, + .pending_soa_cache_cares = SoaPendingCache.empty, + .pending_txt_cache_cares = TxtPendingCache.empty, + .pending_naptr_cache_cares = NaptrPendingCache.empty, + .pending_mx_cache_cares = MxPendingCache.empty, + .pending_caa_cache_cares = CaaPendingCache.empty, + .pending_ns_cache_cares = NSPendingCache.empty, + .pending_ptr_cache_cares = PtrPendingCache.empty, + .pending_cname_cache_cares = CnamePendingCache.empty, + .pending_a_cache_cares = APendingCache.empty, + .pending_aaaa_cache_cares = AAAAPendingCache.empty, + .pending_any_cache_cares = AnyPendingCache.empty, + .pending_addr_cache_cares = AddrPendingCache.empty, + .pending_nameinfo_cache_cares = NameInfoPendingCache.empty, + }; + } + + pub fn init(allocator: std.mem.Allocator, vm: *JSC.VirtualMachine) *DNSResolver { + log("init", .{}); + return DNSResolver.new(.setup(allocator, vm)); } pub fn finalize(this: *DNSResolver) void { @@ -1911,8 +1928,8 @@ pub const DNSResolver = struct { fn anyRequestsPending(this: *DNSResolver) bool { inline for (@typeInfo(DNSResolver).@"struct".fields) |field| { if (comptime std.mem.startsWith(u8, field.name, "pending_")) { - const set = &@field(this, field.name).available; - if (set.count() < set.capacity()) { + const set = &@field(this, field.name).used; + if (set.findFirstSet() != null) { return true; } } @@ -1960,13 +1977,13 @@ pub const DNSResolver = struct { fn getKey(this: *DNSResolver, index: u8, comptime cache_name: []const u8, comptime request_type: type) request_type.PendingCacheKey { var cache = &@field(this, cache_name); - bun.assert(!cache.available.isSet(index)); + bun.assert(cache.used.isSet(index)); const entry = cache.buffer[index]; cache.buffer[index] = undefined; - var available = cache.available; - available.set(index); - cache.available = available; + var used = cache.used; + used.unset(index); + cache.used = used; return entry; } @@ -2221,9 +2238,9 @@ pub const DNSResolver = struct { comptime field: []const u8, ) LookupCacheHit(request_type) { var cache = &@field(this, field); - var inflight_iter = cache.available.iterator( + var inflight_iter = cache.used.iterator( .{ - .kind = .unset, + .kind = .set, }, ); @@ -2249,9 +2266,9 @@ pub const DNSResolver = struct { comptime field: std.meta.FieldEnum(DNSResolver), ) CacheHit { var cache: *PendingCache = &@field(this, @tagName(field)); - var inflight_iter = cache.available.iterator( + var inflight_iter = cache.used.iterator( .{ - .kind = .unset, + .kind = .set, }, ); diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 9aff20eb52..026d1df6fe 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -10733,7 +10733,7 @@ pub const LinkerContext = struct { for (part.scopes) |scope| { r.assignNamesRecursiveWithNumberScope(&r.root, scope, source_index, sorted); } - r.number_scope_pool.hive.available = @TypeOf(r.number_scope_pool.hive.available).initFull(); + r.number_scope_pool.hive.used = @TypeOf(r.number_scope_pool.hive.used).initEmpty(); } } diff --git a/src/hive_array.zig b/src/hive_array.zig index a39e0702e6..0175012b40 100644 --- a/src/hive_array.zig +++ b/src/hive_array.zig @@ -12,21 +12,29 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { const Self = @This(); buffer: [capacity]T, - available: bun.bit_set.IntegerBitSet(capacity), + used: bun.bit_set.IntegerBitSet(capacity), pub const size = capacity; - pub const empty: Self = .{ + + /// This is deliberately a `var` instead of a `const`. + /// + /// https://github.com/ziglang/zig/issues/22462 + /// https://github.com/ziglang/zig/issues/21988 + pub var empty: Self = .{ .buffer = undefined, - .available = .initFull(), + .used = .initEmpty(), }; pub fn init() Self { - return .{}; + return .{ + .buffer = undefined, + .used = .initEmpty(), + }; } pub fn get(self: *Self) ?*T { - const index = self.available.findFirstSet() orelse return null; - self.available.unset(index); + const index = self.used.findFirstUnset() orelse return null; + self.used.set(index); return &self.buffer[index]; } @@ -37,8 +45,8 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { pub fn claim(self: *Self, index: u16) void { assert(index < capacity); - assert(self.available.isSet(index)); - self.available.unset(index); + assert(!self.used.isSet(index)); + self.used.set(index); } pub fn indexOf(self: *const Self, value: *const T) ?u32 { @@ -63,17 +71,17 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { pub fn put(self: *Self, value: *T) bool { const index = self.indexOf(value) orelse return false; - assert(!self.available.isSet(index)); + assert(self.used.isSet(index)); assert(&self.buffer[index] == value); value.* = undefined; - self.available.set(index); + self.used.unset(index); return true; } pub const Fallback = struct { - hive: if (capacity > 0) HiveArray(T, capacity) else void, + hive: if (capacity > 0) Self else void, allocator: std.mem.Allocator, pub const This = @This(); @@ -81,7 +89,7 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { pub fn init(allocator: std.mem.Allocator) This { return .{ .allocator = allocator, - .hive = if (capacity > 0) .empty, + .hive = if (comptime capacity > 0) Self.empty, }; } @@ -156,7 +164,7 @@ test "HiveArray" { try testing.expect(a.in(&d) == false); } - a.available = @TypeOf(a.available).initFull(); + a.used = @TypeOf(a.used).initEmpty(); { for (0..size) |i| { const b = a.get().?; diff --git a/src/http.zig b/src/http.zig index 01a39cb813..0812b7be9d 100644 --- a/src/http.zig +++ b/src/http.zig @@ -583,7 +583,9 @@ fn NewHTTPContext(comptime ssl: bool) type { return ActiveSocket.init(&dead_socket); } - pending_sockets: HiveArray(PooledSocket, pool_size) = .empty, + pub const PooledSocketHiveAllocator = bun.HiveArray(PooledSocket, pool_size); + + pending_sockets: PooledSocketHiveAllocator, us_socket_context: *uws.SocketContext, const Context = @This(); @@ -950,7 +952,7 @@ fn NewHTTPContext(comptime ssl: bool) type { if (hostname.len > MAX_KEEPALIVE_HOSTNAME) return null; - var iter = this.pending_sockets.available.iterator(.{ .kind = .unset }); + var iter = this.pending_sockets.used.iterator(.{ .kind = .set }); while (iter.next()) |pending_socket_index| { var socket = this.pending_sockets.at(@as(u16, @intCast(pending_socket_index))); @@ -1202,9 +1204,11 @@ pub const HTTPThread = struct { .loop = undefined, .http_context = .{ .us_socket_context = undefined, + .pending_sockets = NewHTTPContext(false).PooledSocketHiveAllocator.empty, }, .https_context = .{ .us_socket_context = undefined, + .pending_sockets = NewHTTPContext(true).PooledSocketHiveAllocator.empty, }, .timer = std.time.Timer.start() catch unreachable, }; diff --git a/src/install/install.zig b/src/install/install.zig index b4ce83fff6..8cf3d63649 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -2697,8 +2697,8 @@ pub const PackageManager = struct { pending_pre_calc_hashes: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), pending_tasks: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), total_tasks: u32 = 0, - preallocated_network_tasks: PreallocatedNetworkTasks = .init(bun.default_allocator), - preallocated_resolve_tasks: PreallocatedTaskStore = .init(bun.default_allocator), + preallocated_network_tasks: PreallocatedNetworkTasks, + preallocated_resolve_tasks: PreallocatedTaskStore, /// items are only inserted into this if they took more than 500ms lifecycle_script_time_log: LifecycleScriptTimeLog = .{}, @@ -8960,6 +8960,8 @@ pub const PackageManager = struct { // var progress = Progress{}; // var node = progress.start(name: []const u8, estimated_total_items: usize) manager.* = PackageManager{ + .preallocated_network_tasks = .init(bun.default_allocator), + .preallocated_resolve_tasks = .init(bun.default_allocator), .options = options, .active_lifecycle_scripts = .{ .context = manager, @@ -9127,6 +9129,8 @@ pub const PackageManager = struct { @memcpy(original_package_json_path[top_level_dir_no_trailing_slash.len..][0.."/package.json".len], "/package.json"); manager.* = PackageManager{ + .preallocated_network_tasks = .init(bun.default_allocator), + .preallocated_resolve_tasks = .init(bun.default_allocator), .options = .{ .max_concurrent_lifecycle_scripts = cli.concurrent_scripts orelse cpu_count * 2, }, @@ -14898,7 +14902,7 @@ pub const PackageManager = struct { manager.lockfile.initEmpty(manager.allocator); if (manager.options.enable.frozen_lockfile and load_result != .not_found) { - if (comptime log_level != .silent) { + if (log_level != .silent) { Output.prettyErrorln("error: lockfile had changes, but lockfile is frozen", .{}); } Global.crash(); @@ -14942,9 +14946,9 @@ pub const PackageManager = struct { _ = manager.getTemporaryDirectory(); } - if (comptime log_level.showProgress()) { + if (log_level.showProgress()) { manager.startProgressBar(); - } else if (comptime log_level != .silent) { + } else if (log_level != .silent) { Output.prettyErrorln("Resolving dependencies", .{}); Output.flush(); }