diff options
author | 2022-12-01 02:34:15 -0800 | |
---|---|---|
committer | 2022-12-01 02:34:15 -0800 | |
commit | bddf523ac9d201a1d15aa8b938b516aa1a6949aa (patch) | |
tree | f073b450e989954d7389ea4654bced3e96727d92 | |
parent | 1506a25198ce1c09d102c6a7a88ed24cc2a8be8a (diff) | |
download | bun-bddf523ac9d201a1d15aa8b938b516aa1a6949aa.tar.gz bun-bddf523ac9d201a1d15aa8b938b516aa1a6949aa.tar.zst bun-bddf523ac9d201a1d15aa8b938b516aa1a6949aa.zip |
Reduce memory usage in Bun.serve() by up to 3x (#1569)
* Update WebKit
* Use 5x less memory in Bun.serve()
* Update Dockerfile.devcontainer
* Update async-overhead.mjs
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r-- | .github/workflows/bun-linux-build.yml | 6 | ||||
-rw-r--r-- | .github/workflows/bun-mac-aarch64.yml | 16 | ||||
-rw-r--r-- | .github/workflows/bun-mac-x64-baseline.yml | 16 | ||||
-rw-r--r-- | .github/workflows/bun-mac-x64.yml | 16 | ||||
-rw-r--r-- | Dockerfile.devcontainer | 2 | ||||
-rw-r--r-- | bench/snippets/async-overhead.mjs | 15 | ||||
m--------- | src/bun.js/WebKit | 0 | ||||
-rw-r--r-- | src/bun.js/api/bun/socket.zig | 3 | ||||
-rw-r--r-- | src/bun.js/api/server.zig | 11 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 17 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 13 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.h | 4 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.zig | 2 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 128 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 2 |
16 files changed, 216 insertions, 37 deletions
diff --git a/.github/workflows/bun-linux-build.yml b/.github/workflows/bun-linux-build.yml index db40ed84d..9b6b8234f 100644 --- a/.github/workflows/bun-linux-build.yml +++ b/.github/workflows/bun-linux-build.yml @@ -39,21 +39,21 @@ jobs: arch: x86_64 build_arch: amd64 runner: linux-amd64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-amd64-lto.tar.gz" webkit_basename: "bun-webkit-linux-amd64-lto" - cpu: westmere tag: linux-x64-baseline arch: x86_64 build_arch: amd64 runner: linux-amd64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-amd64-lto.tar.gz" webkit_basename: "bun-webkit-linux-amd64-lto" - cpu: native tag: linux-aarch64 arch: aarch64 build_arch: arm64 runner: linux-arm64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-arm64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-arm64-lto.tar.gz" webkit_basename: "bun-webkit-linux-arm64-lto" steps: diff --git a/.github/workflows/bun-mac-aarch64.yml b/.github/workflows/bun-mac-aarch64.yml index 7956fba37..f6b52e6ed 100644 --- a/.github/workflows/bun-mac-aarch64.yml +++ b/.github/workflows/bun-mac-aarch64.yml @@ -91,7 +91,7 @@ jobs: # obj: bun-obj-darwin-x64-baseline # runner: macos-11 # artifact: bun-obj-darwin-x64-baseline - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: true # compile_obj: false # - cpu: haswell @@ -100,7 +100,7 @@ jobs: # obj: bun-obj-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: true # compile_obj: false # - cpu: westmere @@ -109,7 +109,7 @@ jobs: # obj: bun-obj-darwin-x64-baseline # runner: macos-11 # artifact: bun-obj-darwin-x64-baseline - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: false # compile_obj: true # - cpu: haswell @@ -118,7 +118,7 @@ jobs: # obj: bun-obj-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: false # compile_obj: true - cpu: native @@ -126,7 +126,7 @@ jobs: tag: bun-darwin-aarch64 obj: bun-obj-darwin-aarch64 artifact: bun-obj-darwin-aarch64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz" runner: macos-arm64 dependencies: true compile_obj: true @@ -230,7 +230,7 @@ jobs: # package: bun-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64-baseline - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # - cpu: haswell # arch: x86_64 # tag: bun-darwin-x64 @@ -238,14 +238,14 @@ jobs: # package: bun-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" - cpu: native arch: aarch64 tag: bun-darwin-aarch64 obj: bun-obj-darwin-aarch64 package: bun-darwin-aarch64 artifact: bun-obj-darwin-aarch64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz" runner: macos-arm64 steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/bun-mac-x64-baseline.yml b/.github/workflows/bun-mac-x64-baseline.yml index 0f5f5e8a4..4eb4067f9 100644 --- a/.github/workflows/bun-mac-x64-baseline.yml +++ b/.github/workflows/bun-mac-x64-baseline.yml @@ -91,7 +91,7 @@ jobs: obj: bun-obj-darwin-x64-baseline runner: macos-11 artifact: bun-obj-darwin-x64-baseline - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" dependencies: true compile_obj: false # - cpu: haswell @@ -100,7 +100,7 @@ jobs: # obj: bun-obj-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: true # compile_obj: false - cpu: westmere @@ -109,7 +109,7 @@ jobs: obj: bun-obj-darwin-x64-baseline runner: macos-11 artifact: bun-obj-darwin-x64-baseline - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" dependencies: false compile_obj: true # - cpu: haswell @@ -118,7 +118,7 @@ jobs: # obj: bun-obj-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: false # compile_obj: true # - cpu: native @@ -126,7 +126,7 @@ jobs: # tag: bun-darwin-aarch64 # obj: bun-obj-darwin-aarch64 # artifact: bun-obj-darwin-aarch64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # runner: macos-arm64 # dependencies: true # compile_obj: true @@ -231,7 +231,7 @@ jobs: package: bun-darwin-x64 runner: macos-11 artifact: bun-obj-darwin-x64-baseline - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # - cpu: haswell # arch: x86_64 # tag: bun-darwin-x64 @@ -239,14 +239,14 @@ jobs: # package: bun-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # - cpu: native # arch: aarch64 # tag: bun-darwin-aarch64 # obj: bun-obj-darwin-aarch64 # package: bun-darwin-aarch64 # artifact: bun-obj-darwin-aarch64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # runner: macos-arm64 steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/bun-mac-x64.yml b/.github/workflows/bun-mac-x64.yml index 20db3ef5e..83c8393de 100644 --- a/.github/workflows/bun-mac-x64.yml +++ b/.github/workflows/bun-mac-x64.yml @@ -90,7 +90,7 @@ jobs: # obj: bun-obj-darwin-x64-baseline # runner: macos-11 # artifact: bun-obj-darwin-x64-baseline - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: true # compile_obj: false - cpu: haswell @@ -99,7 +99,7 @@ jobs: obj: bun-obj-darwin-x64 runner: macos-11 artifact: bun-obj-darwin-x64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" dependencies: true compile_obj: false # - cpu: westmere @@ -108,7 +108,7 @@ jobs: # obj: bun-obj-darwin-x64-baseline # runner: macos-11 # artifact: bun-obj-darwin-x64-baseline - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # dependencies: false # compile_obj: true - cpu: haswell @@ -117,7 +117,7 @@ jobs: obj: bun-obj-darwin-x64 runner: macos-11 artifact: bun-obj-darwin-x64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" dependencies: false compile_obj: true # - cpu: native @@ -125,7 +125,7 @@ jobs: # tag: bun-darwin-aarch64 # obj: bun-obj-darwin-aarch64 # artifact: bun-obj-darwin-aarch64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz" # runner: macos-arm64 # dependencies: true # compile_obj: true @@ -232,7 +232,7 @@ jobs: # package: bun-darwin-x64 # runner: macos-11 # artifact: bun-obj-darwin-x64-baseline - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" - cpu: haswell arch: x86_64 tag: bun-darwin-x64 @@ -240,14 +240,14 @@ jobs: package: bun-darwin-x64 runner: macos-11 artifact: bun-obj-darwin-x64 - webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz" + webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz" # - cpu: native # arch: aarch64 # tag: bun-darwin-aarch64 # obj: bun-obj-darwin-aarch64 # package: bun-darwin-aarch64 # artifact: bun-obj-darwin-aarch64 - # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz" + # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz" # runner: macos-arm64 steps: - uses: actions/checkout@v3 diff --git a/Dockerfile.devcontainer b/Dockerfile.devcontainer index 45348a4d6..7d32e9162 100644 --- a/Dockerfile.devcontainer +++ b/Dockerfile.devcontainer @@ -90,7 +90,7 @@ RUN cd $GITHUB_WORKSPACE && \ rm zig-linux-$BUILDARCH.zip; RUN cd $GITHUB_WORKSPACE && \ - curl -o bun-webkit-linux-$BUILDARCH.tar.gz -L https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-$BUILDARCH.tar.gz && \ + curl -o bun-webkit-linux-$BUILDARCH.tar.gz -L https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-$BUILDARCH.tar.gz && \ tar -xzf bun-webkit-linux-$BUILDARCH.tar.gz && \ rm bun-webkit-linux-$BUILDARCH.tar.gz && \ cat $WEBKIT_OUT_DIR/include/cmakeconfig.h > /dev/null diff --git a/bench/snippets/async-overhead.mjs b/bench/snippets/async-overhead.mjs index f05b348e6..bec278b56 100644 --- a/bench/snippets/async-overhead.mjs +++ b/bench/snippets/async-overhead.mjs @@ -5,6 +5,19 @@ bench("async function(){}", async function () {}); bench("await 1", async function () { return await 1; }); + +function callnextTick(resolve) { + process.nextTick(resolve); +} + +function awaitNextTick() { + return new Promise(callnextTick); +} + +bench("promise.nextTick", async function () { + return awaitNextTick(); +}); + bench("await new Promise(resolve => resolve())", async function () { await new Promise((resolve) => resolve()); }); @@ -12,7 +25,7 @@ bench( "Promise.all(Array.from({length: 100}, () => new Promise((resolve) => resolve())))", async function () { return Promise.all(Array.from({ length: 100 }, () => Promise.resolve(1))); - } + }, ); await run(); diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit -Subproject 6e5bbee4a3f5fe81a095067e5d0f8d8f891f79d +Subproject 7e5db543dbeb0f5510c18b83b94c79ca391514f diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 78829f428..fa4411cc0 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -409,6 +409,7 @@ pub const Listener = struct { ctx_opts.ssl_prefer_low_memory_usage = @boolToInt(ssl_config.low_memory_mode); } + globalObject.bunVM().eventLoop().ensureWaker(); socket.socket_context = uws.us_create_socket_context(@boolToInt(ssl_enabled), uws.Loop.get().?, @sizeOf(usize), ctx_opts); if (ssl) |ssl_config| { @@ -661,6 +662,8 @@ pub const Listener = struct { ctx_opts.ssl_prefer_low_memory_usage = @boolToInt(ssl_config.low_memory_mode); } + globalObject.bunVM().eventLoop().ensureWaker(); + var socket_context = uws.us_create_socket_context(@boolToInt(ssl_enabled), uws.Loop.get().?, @sizeOf(usize), ctx_opts).?; var connection: Listener.UnixOrHost = if (port) |port_| .{ .host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), .port = port_ }, diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index fb991481f..e2892f88f 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -4169,6 +4169,8 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { } pub fn onRequestComplete(this: *ThisServer) void { + this.vm.eventLoop().processGCTimer(); + this.pending_requests -= 1; this.deinitIfWeCan(); } @@ -4567,7 +4569,14 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { this.config.hostname; this.ref(); - this.vm.autoGarbageCollect(); + + // Starting up an HTTP server is a good time to GC + if (this.vm.aggressive_garbage_collection == .aggressive) { + this.vm.autoGarbageCollect(); + } else { + this.vm.eventLoop().performGC(); + } + this.app.listenWithConfig(*ThisServer, this, onListen, .{ .port = this.config.port, .host = host, diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index e2e42f38c..3af4b8421 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3432,3 +3432,20 @@ restart: return; } } + +extern "C" size_t JSC__VM__blockBytesAllocated(JSC__VM* vm) +{ +#if ENABLE(RESOURCE_USAGE) + return vm->heap.blockBytesAllocated(); +#else + return 0; +#endif +} +extern "C" size_t JSC__VM__externalMemorySize(JSC__VM* vm) +{ +#if ENABLE(RESOURCE_USAGE) + return vm->heap.externalMemorySize(); +#else + return 0; +#endif +}
\ No newline at end of file diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 15e15b032..d17cc5382 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -3385,7 +3385,18 @@ pub const VM = extern struct { vm, }); } - pub const Extern = [_][]const u8{ "collectAsync", "heapSize", "releaseWeakRefs", "throwError", "doWork", "deferGC", "holdAPILock", "runGC", "generateHeapSnapshot", "isJITEnabled", "deleteAllCode", "create", "deinit", "setExecutionForbidden", "executionForbidden", "isEntered", "throwError", "drainMicrotasks", "whenIdle", "shrinkFootprint", "setExecutionTimeLimit", "clearExecutionTimeLimit" }; + + pub fn externalMemorySize(vm: *VM) usize { + return cppFn("externalMemorySize", .{vm}); + } + + /// `RESOURCE_USAGE` build option in JavaScriptCore is required for this function + /// This is faster than checking the heap size + pub fn blockBytesAllocated(vm: *VM) usize { + return cppFn("blockBytesAllocated", .{vm}); + } + + pub const Extern = [_][]const u8{ "collectAsync", "externalMemorySize", "blockBytesAllocated", "heapSize", "releaseWeakRefs", "throwError", "doWork", "deferGC", "holdAPILock", "runGC", "generateHeapSnapshot", "isJITEnabled", "deleteAllCode", "create", "deinit", "setExecutionForbidden", "executionForbidden", "isEntered", "throwError", "drainMicrotasks", "whenIdle", "shrinkFootprint", "setExecutionTimeLimit", "clearExecutionTimeLimit" }; }; pub const ThrowScope = extern struct { diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h index e35afe0f0..34fdaba99 100644 --- a/src/bun.js/bindings/headers-cpp.h +++ b/src/bun.js/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1669793662 +//-- AUTOGENERATED FILE -- 1669880224 // clang-format off #pragma once diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 399fc54a6..75701502d 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format off -//-- AUTOGENERATED FILE -- 1669793662 +//-- AUTOGENERATED FILE -- 1669880224 #pragma once #include <stddef.h> @@ -337,6 +337,7 @@ CPP_DECL JSC__JSValue JSC__Exception__value(JSC__Exception* arg0); #pragma mark - JSC::VM +CPP_DECL size_t JSC__VM__blockBytesAllocated(JSC__VM* arg0); CPP_DECL void JSC__VM__clearExecutionTimeLimit(JSC__VM* arg0); CPP_DECL void JSC__VM__collectAsync(JSC__VM* arg0); CPP_DECL JSC__VM* JSC__VM__create(unsigned char HeapType0); @@ -346,6 +347,7 @@ CPP_DECL void JSC__VM__deleteAllCode(JSC__VM* arg0, JSC__JSGlobalObject* arg1); CPP_DECL void JSC__VM__doWork(JSC__VM* arg0); CPP_DECL void JSC__VM__drainMicrotasks(JSC__VM* arg0); CPP_DECL bool JSC__VM__executionForbidden(JSC__VM* arg0); +CPP_DECL size_t JSC__VM__externalMemorySize(JSC__VM* arg0); CPP_DECL size_t JSC__VM__heapSize(JSC__VM* arg0); CPP_DECL void JSC__VM__holdAPILock(JSC__VM* arg0, void* arg1, void (* ArgFn2)(void* arg0)); CPP_DECL bool JSC__VM__isEntered(JSC__VM* arg0); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index e99db7ef0..b0bfadae9 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -254,6 +254,7 @@ pub extern fn JSC__JSValue__toZigString(JSValue0: JSC__JSValue, arg1: [*c]ZigStr pub extern fn JSC__Exception__create(arg0: ?*JSC__JSGlobalObject, arg1: [*c]JSC__JSObject, StackCaptureAction2: u8) [*c]JSC__Exception; pub extern fn JSC__Exception__getStackTrace(arg0: [*c]JSC__Exception, arg1: [*c]ZigStackTrace) void; pub extern fn JSC__Exception__value(arg0: [*c]JSC__Exception) JSC__JSValue; +pub extern fn JSC__VM__blockBytesAllocated(arg0: [*c]JSC__VM) usize; pub extern fn JSC__VM__clearExecutionTimeLimit(arg0: [*c]JSC__VM) void; pub extern fn JSC__VM__collectAsync(arg0: [*c]JSC__VM) void; pub extern fn JSC__VM__create(HeapType0: u8) [*c]JSC__VM; @@ -263,6 +264,7 @@ pub extern fn JSC__VM__deleteAllCode(arg0: [*c]JSC__VM, arg1: ?*JSC__JSGlobalObj pub extern fn JSC__VM__doWork(arg0: [*c]JSC__VM) void; pub extern fn JSC__VM__drainMicrotasks(arg0: [*c]JSC__VM) void; pub extern fn JSC__VM__executionForbidden(arg0: [*c]JSC__VM) bool; +pub extern fn JSC__VM__externalMemorySize(arg0: [*c]JSC__VM) usize; pub extern fn JSC__VM__heapSize(arg0: [*c]JSC__VM) usize; pub extern fn JSC__VM__holdAPILock(arg0: [*c]JSC__VM, arg1: ?*anyopaque, ArgFn2: ?fn (?*anyopaque) callconv(.C) void) void; pub extern fn JSC__VM__isEntered(arg0: [*c]JSC__VM) bool; diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index f9f11c943..aac1e3dcd 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -220,6 +220,21 @@ pub const EventLoop = struct { start_server_on_next_tick: bool = false, defer_count: std.atomic.Atomic(usize) = std.atomic.Atomic(usize).init(0), + gc_timer: *uws.Timer = undefined, + gc_last_heap_size: usize = 0, + gc_last_heap_size_on_repeating_timer: usize = 0, + heap_size_didnt_change_for_repeating_timer_ticks_count: u8 = 0, + gc_timer_state: GCTimerState = GCTimerState.pending, + gc_repeating_timer: *uws.Timer = undefined, + gc_timer_interval: i32 = 0, + gc_repeating_timer_fast: bool = true, + + pub const GCTimerState = enum { + pending, + scheduled, + run_on_next_tick, + }; + pub const Queue = std.fifo.LinearFifo(Task, .Dynamic); pub fn tickWithCount(this: *EventLoop) u32 { @@ -333,14 +348,59 @@ pub const EventLoop = struct { var loop = this.virtual_machine.uws_event_loop.?; if (loop.num_polls > 0 or loop.active > 0) { loop.tick(); + this.processGCTimer(); // this.afterUSocketsTick(); } } + pub fn scheduleGCTimer(this: *EventLoop) void { + this.gc_timer_state = .scheduled; + this.gc_timer.set(this, onGCTimer, 16, 0); + } + + pub fn processGCTimer(this: *EventLoop) void { + var vm = this.virtual_machine.global.vm(); + const this_heap_size = vm.blockBytesAllocated(); + const prev = this.gc_last_heap_size; + + switch (this.gc_timer_state) { + .run_on_next_tick => { + if (this_heap_size > prev) { + this.scheduleGCTimer(); + this.updateGCRepeatTimer(.fast); + } else { + this.gc_timer_state = .pending; + } + vm.collectAsync(); + this.gc_last_heap_size = this_heap_size; + }, + .pending => { + if (this_heap_size > prev) { + this.updateGCRepeatTimer(.fast); + + if (this_heap_size > prev * 2) { + this.performGC(); + } else { + this.scheduleGCTimer(); + } + } + }, + .scheduled => { + if (this_heap_size > prev * 2) { + this.updateGCRepeatTimer(.fast); + this.performGC(); + } + }, + } + } + // TODO: fix this technical debt pub fn tick(this: *EventLoop) void { var ctx = this.virtual_machine; this.tickConcurrent(); + + this.processGCTimer(); + var global_vm = ctx.global.vm(); while (true) { while (this.tickWithCount() > 0) { @@ -373,10 +433,6 @@ pub const EventLoop = struct { if (loop.active > 0 or (ctx.us_loop_reference_count > 0 and !ctx.is_us_loop_entered and (loop.num_polls > 0 or this.start_server_on_next_tick))) { if (this.tickConcurrentWithCount() > 0) { this.tick(); - } else { - if ((@intCast(c_ulonglong, ctx.uws_event_loop.?.internal_loop_data.iteration_nr) % 1_000) == 1) { - _ = ctx.global.vm().runGC(true); - } } ctx.is_us_loop_entered = true; @@ -427,11 +483,75 @@ pub const EventLoop = struct { if (this.virtual_machine.uws_event_loop == null) { var actual = uws.Loop.get().?; this.virtual_machine.uws_event_loop = actual; + this.gc_timer = uws.Timer.create(actual, this); + this.gc_repeating_timer = uws.Timer.create(actual, this); + + var gc_timer_interval: i32 = 1000; + if (this.virtual_machine.bundler.env.map.get("BUN_GC_TIMER_INTERVAL")) |timer| { + if (std.fmt.parseInt(i32, timer, 10)) |parsed| { + if (parsed > 0) { + gc_timer_interval = parsed; + } + } else |_| {} + } + this.gc_repeating_timer.set(this, onGCRepeatingTimer, gc_timer_interval, gc_timer_interval); + this.gc_timer_interval = gc_timer_interval; // _ = actual.addPostHandler(*JSC.EventLoop, this, JSC.EventLoop.afterUSocketsTick); // _ = actual.addPreHandler(*JSC.VM, this.virtual_machine.global.vm(), JSC.VM.drainMicrotasks); } } + pub fn onGCTimer(timer: *uws.Timer) callconv(.C) void { + var this = timer.as(*EventLoop); + this.gc_timer_state = .run_on_next_tick; + } + + // We want to always run GC once in awhile + // But if you have a long-running instance of Bun, you don't want the + // program constantly using CPU doing GC for no reason + // + // So we have two settings for this GC timer: + // + // - Fast: GC runs every 1 second + // - Slow: GC runs every 30 seconds + // + // When the heap size is increasing, we always switch to fast mode + // When the heap size has been the same or less for 30 seconds, we switch to slow mode + pub fn updateGCRepeatTimer(this: *EventLoop, comptime setting: @Type(.EnumLiteral)) void { + if (setting == .fast and !this.gc_repeating_timer_fast) { + this.gc_repeating_timer_fast = true; + this.gc_repeating_timer.set(this, onGCRepeatingTimer, this.gc_timer_interval, this.gc_timer_interval); + this.heap_size_didnt_change_for_repeating_timer_ticks_count = 0; + } else if (setting == .slow and this.gc_repeating_timer_fast) { + this.gc_repeating_timer_fast = false; + this.gc_repeating_timer.set(this, onGCRepeatingTimer, 30_000, 30_000); + this.heap_size_didnt_change_for_repeating_timer_ticks_count = 0; + } + } + + pub fn onGCRepeatingTimer(timer: *uws.Timer) callconv(.C) void { + var this = timer.as(*EventLoop); + const prev_heap_size = this.gc_last_heap_size_on_repeating_timer; + this.performGC(); + this.gc_last_heap_size_on_repeating_timer = this.gc_last_heap_size; + if (prev_heap_size == this.gc_last_heap_size_on_repeating_timer) { + this.heap_size_didnt_change_for_repeating_timer_ticks_count +|= 1; + if (this.heap_size_didnt_change_for_repeating_timer_ticks_count >= 30) { + // make the timer interval longer + this.updateGCRepeatTimer(.slow); + } + } else { + this.heap_size_didnt_change_for_repeating_timer_ticks_count = 0; + this.updateGCRepeatTimer(.fast); + } + } + + /// Asynchronously run the garbage collector and track how much memory is now allocated + pub fn performGC(this: *EventLoop) void { + this.global.vm().collectAsync(); + this.gc_last_heap_size = this.global.vm().blockBytesAllocated(); + } + pub fn enqueueTaskConcurrent(this: *EventLoop, task: *ConcurrentTask) void { JSC.markBinding(@src()); diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 6f966df2b..54fec7a96 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -1333,6 +1333,7 @@ pub const VirtualMachine = struct { // pending_internal_promise can change if hot module reloading is enabled if (this.bun_watcher != null) { + this.eventLoop().performGC(); switch (this.pending_internal_promise.status(this.global.vm())) { JSC.JSPromise.Status.Pending => { while (this.pending_internal_promise.status(this.global.vm()) == .Pending) { @@ -1346,6 +1347,7 @@ pub const VirtualMachine = struct { else => {}, } } else { + this.eventLoop().performGC(); this.waitForPromise(promise); } |