Skip to content

Commit

Permalink
perf: optimize immutable signals and props (QwikDev#4079)
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat authored May 6, 2023
1 parent 06be3ff commit 0f98942
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 25 deletions.
15 changes: 12 additions & 3 deletions packages/qwik/src/core/container/pause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
import { HOST_FLAG_DYNAMIC, type QContext, tryGetContext } from '../state/context';
import { SignalImpl } from '../state/signal';
import type { QRL } from '../qrl/qrl.public';
import { QObjectImmutable, QObjectRecursive } from '../state/constants';

/**
* @internal
Expand Down Expand Up @@ -408,7 +409,7 @@ export const _pauseFromContexts = async (
}
const flags = getProxyFlags(obj) ?? 0;
const converted: (Subscriptions | number)[] = [];
if (flags > 0) {
if (flags & QObjectRecursive) {
converted.push(flags);
}
for (const sub of subs) {
Expand Down Expand Up @@ -657,6 +658,7 @@ const collectProps = (elCtx: QContext, collector: Collector) => {
return;
} else {
collectValue(props, collector, false);
collectSubscriptions(getProxyManager(props)!, collector, false);
}
}
}
Expand Down Expand Up @@ -712,6 +714,7 @@ export const collectElementData = (
) => {
if (elCtx.$props$ && !isEmptyObj(elCtx.$props$)) {
collectValue(elCtx.$props$, collector, dynamicCtx);
collectSubscriptions(getProxyManager(elCtx.$props$)!, collector, dynamicCtx);
}
if (elCtx.$componentQrl$) {
collectValue(elCtx.$componentQrl$, collector, dynamicCtx);
Expand Down Expand Up @@ -763,6 +766,9 @@ export const collectSubscriptions = (
collector: Collector,
leaks: boolean | QwikElement
) => {
// if (!leaks) {
// return;
// }
if (collector.$seen$.has(manager)) {
return;
}
Expand All @@ -773,7 +779,7 @@ export const collectSubscriptions = (
for (const key of subs) {
const type = key[0];
if (type > 0) {
collectValue(key[2], collector, true);
collectValue(key[2], collector, leaks);
}
if (leaks === true) {
const host = key[1];
Expand Down Expand Up @@ -844,7 +850,10 @@ export const collectValue = (obj: any, collector: Collector, leaks: boolean | Qw
return;
}
seen.add(obj);
collectSubscriptions(getProxyManager(input)!, collector, leaks);
const mutable = (getProxyFlags(obj)! & QObjectImmutable) === 0;
if (leaks && mutable) {
collectSubscriptions(getProxyManager(input)!, collector, leaks);
}
if (fastWeakSerialize(input)) {
collector.$objSet$.add(obj);
return;
Expand Down
20 changes: 15 additions & 5 deletions packages/qwik/src/core/container/serializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import {
type SubscriberEffect,
} from '../use/use-task';
import { isDocument } from '../util/element';
import { SignalDerived, SignalImpl, SignalWrapper } from '../state/signal';
import {
QObjectSignalFlags,
SIGNAL_IMMUTABLE,
SignalDerived,
SignalImpl,
SignalWrapper,
} from '../state/signal';
import { type Collector, collectSubscriptions, collectValue } from './pause';
import {
fastWeakSerialize,
Expand Down Expand Up @@ -112,13 +118,16 @@ const QRLSerializer: Serializer<QRLInternal> = {
},
};

const WatchSerializer: Serializer<SubscriberEffect> = {
const TaskSerializer: Serializer<SubscriberEffect> = {
$prefix$: '\u0003',
$test$: (v) => isSubscriberDescriptor(v),
$collect$: (v, collector, leaks) => {
collectValue(v.$qrl$, collector, leaks);
if (v.$state$) {
collectValue(v.$state$, collector, leaks);
if (leaks === true && v.$state$ instanceof SignalImpl) {
collectSubscriptions(v.$state$[QObjectManagerSymbol], collector, true);
}
}
},
$serialize$: (obj, getObjId) => serializeWatch(obj, getObjId),
Expand Down Expand Up @@ -272,8 +281,9 @@ const SignalSerializer: Serializer<SignalImpl<any>> = {
$test$: (v) => v instanceof SignalImpl,
$collect$: (obj, collector, leaks) => {
collectValue(obj.untrackedValue, collector, leaks);
if (leaks === true) {
collectSubscriptions(obj[QObjectManagerSymbol], collector, leaks);
const mutable = (obj[QObjectSignalFlags] & SIGNAL_IMMUTABLE) === 0;
if (leaks === true && mutable) {
collectSubscriptions(obj[QObjectManagerSymbol], collector, true);
}
return obj;
},
Expand Down Expand Up @@ -422,7 +432,7 @@ const serializers: Serializer<any>[] = [
QRLSerializer, ////////////// \u0002
SignalSerializer, /////////// \u0012
SignalWrapperSerializer, //// \u0013
WatchSerializer, //////////// \u0003
TaskSerializer, //////////// \u0003
ResourceSerializer, ///////// \u0004
URLSerializer, ////////////// \u0005
DateSerializer, ///////////// \u0006
Expand Down
4 changes: 3 additions & 1 deletion packages/qwik/src/core/state/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { directGetAttribute } from '../render/fast-calls';
import { isElement } from '../../testing/html';
import { assertQwikElement, assertTrue } from '../error/assert';
import { QScopedStyle } from '../util/markers';
import { createPropsState, createProxy } from './store';
import { createPropsState, createProxy, setObjectFlags } from './store';
import { QObjectImmutable } from './constants';

export const Q_CTX = '_qc_';

Expand Down Expand Up @@ -104,6 +105,7 @@ export const getContext = (el: QwikElement, containerState: ContainerState): QCo
}
if (props) {
elCtx.$props$ = getObject(props);
setObjectFlags(elCtx.$props$!, QObjectImmutable);
} else {
elCtx.$props$ = createProxy(createPropsState(), containerState);
}
Expand Down
29 changes: 13 additions & 16 deletions packages/qwik/src/core/state/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,26 +121,23 @@ export class ReadWriteProxyHandler implements ProxyHandler<TargetType> {
assertNumber(flags, 'flags must be an number');
const invokeCtx = tryGetInvokeContext();
const recursive = (flags & QObjectRecursive) !== 0;
const immutable = (flags & QObjectImmutable) !== 0;
let value = target[prop];
if (invokeCtx) {
subscriber = invokeCtx.$subscriber$;
}
if (immutable) {
const hiddenSignal = target[_IMMUTABLE_PREFIX + prop];
const immutableMeta = target[_IMMUTABLE]?.[prop];
if (
!(prop in target) ||
!!hiddenSignal ||
isSignal(immutableMeta) ||
immutableMeta === _IMMUTABLE
) {
subscriber = null;
}
if (hiddenSignal) {
assertTrue(isSignal(hiddenSignal), '$$ prop must be a signal');
value = hiddenSignal.value;
}
const hiddenSignal = target[_IMMUTABLE_PREFIX + prop];
const immutableMeta = target[_IMMUTABLE]?.[prop];
if (
!(prop in target) ||
!!hiddenSignal ||
isSignal(immutableMeta) ||
immutableMeta === _IMMUTABLE
) {
subscriber = null;
}
if (hiddenSignal) {
assertTrue(isSignal(hiddenSignal), '$$ prop must be a signal');
value = hiddenSignal.value;
}
if (subscriber) {
const isA = isArray(target);
Expand Down

0 comments on commit 0f98942

Please sign in to comment.