From bd05883c0bcca06d15565cb4efb612c83472b0e3 Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:50:28 +0900 Subject: [PATCH] Avoid copying FiberRefs for unchanged updates --- .changeset/fuzzy-spiders-smile.md | 5 ++++ packages/effect/src/internal/fiberRefs.ts | 36 +++++++++++++++-------- packages/effect/test/FiberRefs.test.ts | 26 ++++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 .changeset/fuzzy-spiders-smile.md diff --git a/.changeset/fuzzy-spiders-smile.md b/.changeset/fuzzy-spiders-smile.md new file mode 100644 index 00000000000..dcc2681fa7f --- /dev/null +++ b/.changeset/fuzzy-spiders-smile.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Avoid copying all FiberRefs when an update keeps the current fiber's value unchanged. diff --git a/packages/effect/src/internal/fiberRefs.ts b/packages/effect/src/internal/fiberRefs.ts index 8d9ece313bb..ddaafc84f21 100644 --- a/packages/effect/src/internal/fiberRefs.ts +++ b/packages/effect/src/internal/fiberRefs.ts @@ -210,45 +210,57 @@ export const updateAs = dual< readonly fiberRef: FiberRef.FiberRef readonly value: A }) => { + const newStack = updatedStack(self.locals, fiberId, fiberRef, value) + if (newStack === undefined) { + return self + } if (self.locals.size === 0) { - return new FiberRefsImpl(new Map([[fiberRef, [[fiberId, value] as const]]])) + return new FiberRefsImpl(new Map([[fiberRef, newStack]])) } const locals = new Map(self.locals) - unsafeUpdateAs(locals, fiberId, fiberRef, value) + locals.set(fiberRef, newStack) return new FiberRefsImpl(locals) }) -const unsafeUpdateAs = ( +const updatedStack = ( locals: Map, Arr.NonEmptyReadonlyArray>, fiberId: FiberId.Single, fiberRef: FiberRef.FiberRef, value: any -) => { +): Arr.NonEmptyReadonlyArray | undefined => { const oldStack: ReadonlyArray = locals.get(fiberRef) ?? [] - let newStack: Arr.NonEmptyReadonlyArray | undefined - if (Arr.isNonEmptyReadonlyArray(oldStack)) { const [currentId, currentValue] = Arr.headNonEmpty(oldStack) if (currentId[Equal.symbol](fiberId)) { if (Equal.equals(currentValue, value)) { - return + return undefined } else { - newStack = [ + return [ [fiberId, value] as const, ...oldStack.slice(1) ] } } else { - newStack = [ + return [ [fiberId, value] as const, ...oldStack ] } - } else { - newStack = [[fiberId, value] as const] } - locals.set(fiberRef, newStack) + return [[fiberId, value] as const] +} + +const unsafeUpdateAs = ( + locals: Map, Arr.NonEmptyReadonlyArray>, + fiberId: FiberId.Single, + fiberRef: FiberRef.FiberRef, + value: any +) => { + const newStack = updatedStack(locals, fiberId, fiberRef, value) + if (newStack !== undefined) { + locals.set(fiberRef, newStack) + } } /** @internal */ diff --git a/packages/effect/test/FiberRefs.test.ts b/packages/effect/test/FiberRefs.test.ts index e6a77032e7b..c0685ef8468 100644 --- a/packages/effect/test/FiberRefs.test.ts +++ b/packages/effect/test/FiberRefs.test.ts @@ -33,6 +33,32 @@ describe("FiberRefs", () => { deepStrictEqual(FiberRefs.get(newParentFiberRefs, FiberRef.interruptedCause), Option.some(Cause.empty)) }) + it("updateAs reuses unchanged FiberRefs for the same fiber", () => { + const fiberId = FiberId.make(1, 0) as FiberId.Runtime + const childId = FiberId.make(2, 1) as FiberId.Runtime + const fiberRef = FiberRef.unsafeMake(0) + const fiberRefs = FiberRefs.updateAs(FiberRefs.empty(), { + fiberId, + fiberRef, + value: 1 + }) + + const unchanged = FiberRefs.updateAs(fiberRefs, { + fiberId, + fiberRef, + value: 1 + }) + strictEqual(unchanged, fiberRefs) + + const childFiberRefs = FiberRefs.updateAs(fiberRefs, { + fiberId: childId, + fiberRef, + value: 1 + }) + assertTrue(childFiberRefs !== fiberRefs) + strictEqual(childFiberRefs.locals.get(fiberRef)?.length, 2) + }) + describe("currentLogAnnotations", () => { it("doesnt leak", () => { Effect.void.pipe(Effect.annotateLogs("test", "abc"), Effect.runSync)