Skip to content

Commit

Permalink
perf: improve runtime performance (#1986)
Browse files Browse the repository at this point in the history
* chore: add sandbox for runtime perf profiling

* perf: add memo on runtime stringifying fns

* chore: update memo for recipes

* docs: update changelog

* chore: fix rebase & update snapshots

---------

Co-authored-by: Segun Adebayo <[email protected]>
  • Loading branch information
astahmer and segunadebayo authored Jan 12, 2024
1 parent 803a863 commit 74ac0d9
Show file tree
Hide file tree
Showing 30 changed files with 649 additions and 760 deletions.
10 changes: 10 additions & 0 deletions .changeset/spicy-pears-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@pandacss/generator': patch
'@pandacss/shared': patch
---

Improve the performance of the runtime transform functions by caching their results (css, cva, sva, recipe/slot recipe,
patterns)

> See detailed breakdown of the performance improvements
> [here](https://github.com/chakra-ui/panda/pull/1986#issuecomment-1887459483) based on the React Profiler.
28 changes: 14 additions & 14 deletions packages/generator/__tests__/generate-recipe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe('generate recipes', () => {
export declare const textStyle: TextStyleRecipe",
"js": "import { splitProps } from '../helpers.mjs';
"js": "import { memo, splitProps } from '../helpers.mjs';
import { createRecipe, mergeRecipes } from './create-recipe.mjs';
const textStyleFn = /* @__PURE__ */ createRecipe('textStyle', {}, [])
Expand All @@ -146,7 +146,7 @@ describe('generate recipes', () => {
const textStyleVariantKeys = Object.keys(textStyleVariantMap)
export const textStyle = /* @__PURE__ */ Object.assign(textStyleFn, {
export const textStyle = /* @__PURE__ */ Object.assign(memo(textStyleFn), {
__recipe__: true,
__name__: 'textStyle',
raw: (props) => props,
Expand Down Expand Up @@ -188,7 +188,7 @@ describe('generate recipes', () => {
export declare const tooltipStyle: TooltipStyleRecipe",
"js": "import { splitProps } from '../helpers.mjs';
"js": "import { memo, splitProps } from '../helpers.mjs';
import { createRecipe, mergeRecipes } from './create-recipe.mjs';
const tooltipStyleFn = /* @__PURE__ */ createRecipe('tooltipStyle', {}, [])
Expand All @@ -197,7 +197,7 @@ describe('generate recipes', () => {
const tooltipStyleVariantKeys = Object.keys(tooltipStyleVariantMap)
export const tooltipStyle = /* @__PURE__ */ Object.assign(tooltipStyleFn, {
export const tooltipStyle = /* @__PURE__ */ Object.assign(memo(tooltipStyleFn), {
__recipe__: true,
__name__: 'tooltipStyle',
raw: (props) => props,
Expand Down Expand Up @@ -239,7 +239,7 @@ describe('generate recipes', () => {
export declare const cardStyle: CardStyleRecipe",
"js": "import { splitProps } from '../helpers.mjs';
"js": "import { memo, splitProps } from '../helpers.mjs';
import { createRecipe, mergeRecipes } from './create-recipe.mjs';
const cardStyleFn = /* @__PURE__ */ createRecipe('card', {}, [])
Expand All @@ -252,7 +252,7 @@ describe('generate recipes', () => {
const cardStyleVariantKeys = Object.keys(cardStyleVariantMap)
export const cardStyle = /* @__PURE__ */ Object.assign(cardStyleFn, {
export const cardStyle = /* @__PURE__ */ Object.assign(memo(cardStyleFn), {
__recipe__: true,
__name__: 'cardStyle',
raw: (props) => props,
Expand Down Expand Up @@ -295,7 +295,7 @@ describe('generate recipes', () => {
export declare const buttonStyle: ButtonStyleRecipe",
"js": "import { splitProps } from '../helpers.mjs';
"js": "import { memo, splitProps } from '../helpers.mjs';
import { createRecipe, mergeRecipes } from './create-recipe.mjs';
const buttonStyleFn = /* @__PURE__ */ createRecipe('buttonStyle', {
Expand All @@ -316,7 +316,7 @@ describe('generate recipes', () => {
const buttonStyleVariantKeys = Object.keys(buttonStyleVariantMap)
export const buttonStyle = /* @__PURE__ */ Object.assign(buttonStyleFn, {
export const buttonStyle = /* @__PURE__ */ Object.assign(memo(buttonStyleFn), {
__recipe__: true,
__name__: 'buttonStyle',
raw: (props) => props,
Expand Down Expand Up @@ -358,7 +358,7 @@ describe('generate recipes', () => {
export declare const checkbox: CheckboxRecipe",
"js": "import { splitProps, getSlotCompoundVariant } from '../helpers.mjs';
"js": "import { getSlotCompoundVariant, memo, splitProps } from '../helpers.mjs';
import { createRecipe } from './create-recipe.mjs';
const checkboxDefaultVariants = {
Expand All @@ -382,9 +382,9 @@ describe('generate recipes', () => {
]
const checkboxSlotFns = /* @__PURE__ */ checkboxSlotNames.map(([slotName, slotKey]) => [slotName, createRecipe(slotKey, checkboxDefaultVariants, getSlotCompoundVariant(checkboxCompoundVariants, slotName))])
const checkboxFn = (props = {}) => {
const checkboxFn = memo((props = {}) => {
return Object.fromEntries(checkboxSlotFns.map(([slotName, slotFn]) => [slotName, slotFn(props)]))
}
})
const checkboxVariantKeys = [
\\"size\\"
Expand Down Expand Up @@ -436,7 +436,7 @@ describe('generate recipes', () => {
export declare const badge: BadgeRecipe",
"js": "import { splitProps, getSlotCompoundVariant } from '../helpers.mjs';
"js": "import { getSlotCompoundVariant, memo, splitProps } from '../helpers.mjs';
import { createRecipe } from './create-recipe.mjs';
const badgeDefaultVariants = {}
Expand Down Expand Up @@ -464,9 +464,9 @@ describe('generate recipes', () => {
]
const badgeSlotFns = /* @__PURE__ */ badgeSlotNames.map(([slotName, slotKey]) => [slotName, createRecipe(slotKey, badgeDefaultVariants, getSlotCompoundVariant(badgeCompoundVariants, slotName))])
const badgeFn = (props = {}) => {
const badgeFn = memo((props = {}) => {
return Object.fromEntries(badgeSlotFns.map(([slotName, slotFn]) => [slotName, slotFn(props)]))
}
})
const badgeVariantKeys = [
\\"size\\",
Expand Down

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/generator/src/artifacts/js/cva.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { outdent } from 'outdent'
export function generateCvaFn(ctx: Context) {
return {
js: outdent`
${ctx.file.import('compact, mergeProps, splitProps, uniq', '../helpers')}
${ctx.file.import('compact, mergeProps, memo, splitProps, uniq', '../helpers')}
${ctx.file.import('css, mergeCss', './css')}
const defaults = (conf) => ({
Expand Down Expand Up @@ -55,7 +55,7 @@ export function generateCvaFn(ctx: Context) {
const variantMap = Object.fromEntries(Object.entries(variants).map(([key, value]) => [key, Object.keys(value)]))
return Object.assign(cvaFn, {
return Object.assign(memo(cvaFn), {
__cva__: true,
variantMap,
variantKeys,
Expand Down
10 changes: 5 additions & 5 deletions packages/generator/src/artifacts/js/recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export function generateRecipes(ctx: Context, filters?: ArtifactFilters) {
.when(
Recipes.isSlotRecipeConfig,
(config) => outdent`
${ctx.file.import('splitProps, getSlotCompoundVariant', '../helpers')}
${ctx.file.import('getSlotCompoundVariant, memo, splitProps', '../helpers')}
${ctx.file.import('createRecipe', './create-recipe')}
const ${baseName}DefaultVariants = ${stringify(defaultVariants ?? {})}
Expand All @@ -131,9 +131,9 @@ export function generateRecipes(ctx: Context, filters?: ArtifactFilters) {
const ${baseName}SlotNames = ${stringify(config.slots.map((slot) => [slot, `${config.className}__${slot}`]))}
const ${baseName}SlotFns = /* @__PURE__ */ ${baseName}SlotNames.map(([slotName, slotKey]) => [slotName, createRecipe(slotKey, ${baseName}DefaultVariants, getSlotCompoundVariant(${baseName}CompoundVariants, slotName))])
const ${baseName}Fn = (props = {}) => {
const ${baseName}Fn = memo((props = {}) => {
return Object.fromEntries(${baseName}SlotFns.map(([slotName, slotFn]) => [slotName, slotFn(props)]))
}
})
const ${baseName}VariantKeys = ${stringify(Object.keys(variantKeyMap))}
Expand All @@ -151,7 +151,7 @@ export function generateRecipes(ctx: Context, filters?: ArtifactFilters) {
)
.otherwise(
(config) => outdent`
${ctx.file.import('splitProps', '../helpers')}
${ctx.file.import('memo, splitProps', '../helpers')}
${ctx.file.import('createRecipe, mergeRecipes', './create-recipe')}
const ${baseName}Fn = /* @__PURE__ */ createRecipe('${config.className}', ${stringify(
Expand All @@ -162,7 +162,7 @@ export function generateRecipes(ctx: Context, filters?: ArtifactFilters) {
const ${baseName}VariantKeys = Object.keys(${baseName}VariantMap)
export const ${baseName} = /* @__PURE__ */ Object.assign(${baseName}Fn, {
export const ${baseName} = /* @__PURE__ */ Object.assign(memo(${baseName}Fn), {
__recipe__: true,
__name__: '${baseName}',
raw: (props) => props,
Expand Down
4 changes: 2 additions & 2 deletions packages/generator/src/artifacts/js/sva.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { outdent } from 'outdent'
export function generateSvaFn(ctx: Context) {
return {
js: outdent`
${ctx.file.import('getSlotRecipes, splitProps', '../helpers')}
${ctx.file.import('getSlotRecipes, memo, splitProps', '../helpers')}
${ctx.file.import('cva', './cva')}
export function sva(config) {
Expand All @@ -31,7 +31,7 @@ export function generateSvaFn(ctx: Context) {
Object.entries(variants).map(([key, value]) => [key, Object.keys(value)])
);
return Object.assign(svaFn, {
return Object.assign(memo(svaFn), {
__cva__: false,
raw,
variantMap,
Expand Down
7 changes: 4 additions & 3 deletions packages/shared/src/classname.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { filterBaseConditions } from './condition'
import { isImportant, withoutImportant } from './css-important'
import { toHash } from './hash'
import { mergeProps } from './merge-props'
import { memo } from './memo'
import { normalizeShorthand, normalizeStyleObject } from './normalize-style-object'
import { walkObject } from './walk-object'

Expand Down Expand Up @@ -53,7 +54,7 @@ export function createCss(context: CreateCssContext) {
return result
}

return (styleObject: Record<string, any> = {}) => {
return memo((styleObject: Record<string, any> = {}) => {
const normalizedObject = normalizeStyleObject(styleObject, context)
const classNames = new Set<string>()

Expand All @@ -74,7 +75,7 @@ export function createCss(context: CreateCssContext) {
})

return Array.from(classNames).join(' ')
}
})
}

interface StyleObject {
Expand All @@ -100,5 +101,5 @@ export function createMergeCss(context: CreateCssContext) {
return Object.assign({}, ...resolve(styles))
}

return { mergeCss, assignCss }
return { mergeCss: memo(mergeCss), assignCss }
}
4 changes: 2 additions & 2 deletions packages/studio/styled-system/css/cva.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { compact, mergeProps, splitProps, uniq } from '../helpers.mjs';
import { compact, mergeProps, memo, splitProps, uniq } from '../helpers.mjs';
import { css, mergeCss } from './css.mjs';

const defaults = (conf) => ({
Expand Down Expand Up @@ -49,7 +49,7 @@ export function cva(config) {

const variantMap = Object.fromEntries(Object.entries(variants).map(([key, value]) => [key, Object.keys(value)]))

return Object.assign(cvaFn, {
return Object.assign(memo(cvaFn), {
__cva__: true,
variantMap,
variantKeys,
Expand Down
4 changes: 2 additions & 2 deletions packages/studio/styled-system/css/sva.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getSlotRecipes, splitProps } from '../helpers.mjs';
import { getSlotRecipes, memo, splitProps } from '../helpers.mjs';
import { cva } from './cva.mjs';

export function sva(config) {
Expand All @@ -25,7 +25,7 @@ export function sva(config) {
Object.entries(variants).map(([key, value]) => [key, Object.keys(value)])
);

return Object.assign(svaFn, {
return Object.assign(memo(svaFn), {
__cva__: false,
raw,
variantMap,
Expand Down
36 changes: 18 additions & 18 deletions packages/studio/styled-system/helpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ function mergeProps(...sources) {
}, {});
}

// src/memo.ts
var memo = (fn) => {
const cache = /* @__PURE__ */ new Map();
const get = (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
return get;
};

// src/walk-object.ts
var isNotNullish = (element) => element != null;
function walkObject(target, predicate, options = {}) {
Expand Down Expand Up @@ -150,7 +165,7 @@ function createCss(context) {
}
return result;
};
return (styleObject = {}) => {
return memo((styleObject = {}) => {
const normalizedObject = normalizeStyleObject(styleObject, context);
const classNames = /* @__PURE__ */ new Set();
walkObject(normalizedObject, (value, paths) => {
Expand All @@ -166,7 +181,7 @@ function createCss(context) {
classNames.add(className);
});
return Array.from(classNames).join(" ");
};
});
}
function compactStyles(...styles) {
return styles.filter((style) => isObject(style) && Object.keys(compact(style)).length > 0);
Expand All @@ -184,24 +199,9 @@ function createMergeCss(context) {
function assignCss(...styles) {
return Object.assign({}, ...resolve(styles));
}
return { mergeCss, assignCss };
return { mergeCss: memo(mergeCss), assignCss };
}

// src/memo.ts
var memo = (fn) => {
const cache = /* @__PURE__ */ new Map();
const get = (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
return get;
};

// src/hypenate-property.ts
var wordRegex = /([A-Z])/g;
var msRegex = /^ms-/;
Expand Down
Loading

3 comments on commit 74ac0d9

@vercel
Copy link

@vercel vercel bot commented on 74ac0d9 Jan 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

panda-studio – ./

panda-studio-git-main-chakra-ui.vercel.app
panda-studio-chakra-ui.vercel.app
panda-app.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 74ac0d9 Jan 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

panda-docs – ./website

panda-docs.vercel.app
panda-docs-git-main-chakra-ui.vercel.app
panda-docs-chakra-ui.vercel.app
panda-css.com

@vercel
Copy link

@vercel vercel bot commented on 74ac0d9 Jan 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.