New vs cached object (v8)

Revision 8 of this benchmark created on


Preparation HTML

<script>
const iterations = 128; // Adjust this number to find the tipping point

// Setup
const tailwindClasses = [
  // Typography
  'font-sans', 'font-serif', 'font-mono',
  'text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl',
  'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'font-extrabold', 'font-black',
  'italic', 'not-italic',
  'uppercase', 'lowercase', 'capitalize', 'normal-case',
  'text-left', 'text-center', 'text-right', 'text-justify',

  // Colors (examples for text and background)
  'text-black', 'text-white', 'text-gray-500', 'text-red-500', 'text-blue-500', 'text-green-500',
  'bg-black', 'bg-white', 'bg-gray-100', 'bg-red-100', 'bg-blue-100', 'bg-green-100',

  // Spacing
  'm-0', 'm-1', 'm-2', 'm-4', 'm-8',
  'p-0', 'p-1', 'p-2', 'p-4', 'p-8',
  'mt-1', 'mr-2', 'mb-4', 'ml-8',
  'pt-1', 'pr-2', 'pb-4', 'pl-8',

  // Sizing
  'w-full', 'w-1/2', 'w-1/3', 'w-1/4',
  'h-full', 'h-screen', 'h-64', 'h-32',

  // Flexbox
  'flex', 'inline-flex', 'flex-row', 'flex-col',
  'justify-start', 'justify-center', 'justify-end', 'justify-between', 'justify-around',
  'items-start', 'items-center', 'items-end', 'items-stretch',

  // Grid
  'grid', 'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4',
  'gap-1', 'gap-2', 'gap-4', 'gap-8',

  // Borders
  'border', 'border-2', 'border-4', 'border-8',
  'border-t', 'border-r', 'border-b', 'border-l',
  'rounded-none', 'rounded-sm', 'rounded', 'rounded-md', 'rounded-lg', 'rounded-full',

  // Effects
  'shadow-sm', 'shadow', 'shadow-md', 'shadow-lg', 'shadow-xl', 'shadow-2xl',
  'opacity-25', 'opacity-50', 'opacity-75', 'opacity-100',

  // Transitions
  'transition', 'transition-colors', 'transition-opacity', 'transition-shadow', 'transition-transform',
  'duration-75', 'duration-100', 'duration-150', 'duration-200', 'duration-300',
  'ease-linear', 'ease-in', 'ease-out', 'ease-in-out',

  // Transforms
  'scale-90', 'scale-100', 'scale-110',
  'rotate-45', 'rotate-90', 'rotate-180',

  // Interactivity
  'cursor-pointer', 'cursor-not-allowed',
  'select-none', 'select-text', 'select-all',

  // SVG
  'fill-current', 'stroke-current'
];

function getRandomClasses(count) {
  const classes = new Set();
  while (classes.size < count) {
    classes.add(tailwindClasses[Math.floor(Math.random() * tailwindClasses.length)]);
  }
  return Array.from(classes);
}

// Extended composeStyle function
function composeStyle(styleString) {
  switch (styleString) {
    // Typography
    case 'font-sans': return { fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"' };
    case 'font-serif': return { fontFamily: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif' };
    case 'font-mono': return { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace' };
    case 'text-xs': return { fontSize: '0.75rem', lineHeight: '1rem' };
    case 'text-sm': return { fontSize: '0.875rem', lineHeight: '1.25rem' };
    case 'text-base': return { fontSize: '1rem', lineHeight: '1.5rem' };
    case 'text-lg': return { fontSize: '1.125rem', lineHeight: '1.75rem' };
    case 'text-xl': return { fontSize: '1.25rem', lineHeight: '1.75rem' };
    case 'text-2xl': return { fontSize: '1.5rem', lineHeight: '2rem' };
    case 'text-3xl': return { fontSize: '1.875rem', lineHeight: '2.25rem' };
    case 'font-thin': return { fontWeight: '100' };
    case 'font-extralight': return { fontWeight: '200' };
    case 'font-light': return { fontWeight: '300' };
    case 'font-normal': return { fontWeight: '400' };
    case 'font-medium': return { fontWeight: '500' };
    case 'font-semibold': return { fontWeight: '600' };
    case 'font-bold': return { fontWeight: '700' };
    case 'font-extrabold': return { fontWeight: '800' };
    case 'font-black': return { fontWeight: '900' };
    case 'italic': return { fontStyle: 'italic' };
    case 'not-italic': return { fontStyle: 'normal' };
    case 'uppercase': return { textTransform: 'uppercase' };
    case 'lowercase': return { textTransform: 'lowercase' };
    case 'capitalize': return { textTransform: 'capitalize' };
    case 'normal-case': return { textTransform: 'none' };
    case 'text-left': return { textAlign: 'left' };
    case 'text-center': return { textAlign: 'center' };
    case 'text-right': return { textAlign: 'right' };
    case 'text-justify': return { textAlign: 'justify' };

    // Colors
    case 'text-black': return { color: '#000000' };
    case 'text-white': return { color: '#ffffff' };
    case 'text-gray-500': return { color: '#6b7280' };
    case 'text-red-500': return { color: '#ef4444' };
    case 'text-blue-500': return { color: '#3b82f6' };
    case 'text-green-500': return { color: '#10b981' };
    case 'bg-black': return { backgroundColor: '#000000' };
    case 'bg-white': return { backgroundColor: '#ffffff' };
    case 'bg-gray-100': return { backgroundColor: '#f3f4f6' };
    case 'bg-red-100': return { backgroundColor: '#fee2e2' };
    case 'bg-blue-100': return { backgroundColor: '#dbeafe' };
    case 'bg-green-100': return { backgroundColor: '#d1fae5' };

    // Spacing
    case 'm-0': return { margin: '0px' };
    case 'm-1': return { margin: '0.25rem' };
    case 'm-2': return { margin: '0.5rem' };
    case 'm-4': return { margin: '1rem' };
    case 'm-8': return { margin: '2rem' };
    case 'p-0': return { padding: '0px' };
    case 'p-1': return { padding: '0.25rem' };
    case 'p-2': return { padding: '0.5rem' };
    case 'p-4': return { padding: '1rem' };
    case 'p-8': return { padding: '2rem' };
    case 'mt-1': return { marginTop: '0.25rem' };
    case 'mr-2': return { marginRight: '0.5rem' };
    case 'mb-4': return { marginBottom: '1rem' };
    case 'ml-8': return { marginLeft: '2rem' };
    case 'pt-1': return { paddingTop: '0.25rem' };
    case 'pr-2': return { paddingRight: '0.5rem' };
    case 'pb-4': return { paddingBottom: '1rem' };
    case 'pl-8': return { paddingLeft: '2rem' };

    // Sizing
    case 'w-full': return { width: '100%' };
    case 'w-1/2': return { width: '50%' };
    case 'w-1/3': return { width: '33.333333%' };
    case 'w-1/4': return { width: '25%' };
    case 'h-full': return { height: '100%' };
    case 'h-screen': return { height: '100vh' };
    case 'h-64': return { height: '16rem' };
    case 'h-32': return { height: '8rem' };

    // Flexbox
    case 'flex': return { display: 'flex' };
    case 'inline-flex': return { display: 'inline-flex' };
    case 'flex-row': return { flexDirection: 'row' };
    case 'flex-col': return { flexDirection: 'column' };
    case 'justify-start': return { justifyContent: 'flex-start' };
    case 'justify-center': return { justifyContent: 'center' };
    case 'justify-end': return { justifyContent: 'flex-end' };
    case 'justify-between': return { justifyContent: 'space-between' };
    case 'justify-around': return { justifyContent: 'space-around' };
    case 'items-start': return { alignItems: 'flex-start' };
    case 'items-center': return { alignItems: 'center' };
    case 'items-end': return { alignItems: 'flex-end' };
    case 'items-stretch': return { alignItems: 'stretch' };

    // Grid
    case 'grid': return { display: 'grid' };
    case 'grid-cols-1': return { gridTemplateColumns: 'repeat(1, minmax(0, 1fr))' };
    case 'grid-cols-2': return { gridTemplateColumns: 'repeat(2, minmax(0, 1fr))' };
    case 'grid-cols-3': return { gridTemplateColumns: 'repeat(3, minmax(0, 1fr))' };
    case 'grid-cols-4': return { gridTemplateColumns: 'repeat(4, minmax(0, 1fr))' };
    case 'gap-1': return { gap: '0.25rem' };
    case 'gap-2': return { gap: '0.5rem' };
    case 'gap-4': return { gap: '1rem' };
    case 'gap-8': return { gap: '2rem' };

    // Borders
    case 'border': return { borderWidth: '1px' };
    case 'border-2': return { borderWidth: '2px' };
    case 'border-4': return { borderWidth: '4px' };
    case 'border-8': return { borderWidth: '8px' };
    case 'border-t': return { borderTopWidth: '1px' };
    case 'border-r': return { borderRightWidth: '1px' };
    case 'border-b': return { borderBottomWidth: '1px' };
    case 'border-l': return { borderLeftWidth: '1px' };
    case 'rounded-none': return { borderRadius: '0' };
    case 'rounded-sm': return { borderRadius: '0.125rem' };
    case 'rounded': return { borderRadius: '0.25rem' };
    case 'rounded-md': return { borderRadius: '0.375rem' };
    case 'rounded-lg': return { borderRadius: '0.5rem' };
    case 'rounded-full': return { borderRadius: '9999px' };

    // Effects
    case 'shadow-sm': return { boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)' };
    case 'shadow': return { boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)' };
    case 'shadow-md': return { boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)' };
    case 'shadow-lg': return { boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)' };
    case 'shadow-xl': return { boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)' };
    case 'shadow-2xl': return { boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)' };
    case 'opacity-25': return { opacity: '0.25' };
    case 'opacity-50': return { opacity: '0.5' };
    case 'opacity-75': return { opacity: '0.75' };
    case 'opacity-100': return { opacity: '1' };

    // Transitions
    case 'transition': return { transition: 'all' };
    case 'transition-colors': return { transitionProperty: 'color, background-color, border-color, text-decoration-color, fill, stroke' };
    case 'transition-opacity': return { transitionProperty: 'opacity' };
    case 'transition-shadow': return { transitionProperty: 'box-shadow' };
    case 'transition-transform': return { transitionProperty: 'transform' };
        case 'duration-75': return { transitionDuration: '75ms' };
    case 'duration-100': return { transitionDuration: '100ms' };
    case 'duration-150': return { transitionDuration: '150ms' };
    case 'duration-200': return { transitionDuration: '200ms' };
    case 'duration-300': return { transitionDuration: '300ms' };
    case 'ease-linear': return { transitionTimingFunction: 'linear' };
    case 'ease-in': return { transitionTimingFunction: 'cubic-bezier(0.4, 0, 1, 1)' };
    case 'ease-out': return { transitionTimingFunction: 'cubic-bezier(0, 0, 0.2, 1)' };
    case 'ease-in-out': return { transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)' };

    // Transforms
    case 'scale-90': return { transform: 'scale(0.9)' };
    case 'scale-100': return { transform: 'scale(1)' };
    case 'scale-110': return { transform: 'scale(1.1)' };
    case 'rotate-45': return { transform: 'rotate(45deg)' };
    case 'rotate-90': return { transform: 'rotate(90deg)' };
    case 'rotate-180': return { transform: 'rotate(180deg)' };

    // Interactivity
    case 'cursor-pointer': return { cursor: 'pointer' };
    case 'cursor-not-allowed': return { cursor: 'not-allowed' };
    case 'select-none': return { userSelect: 'none' };
    case 'select-text': return { userSelect: 'text' };
    case 'select-all': return { userSelect: 'all' };

    // SVG
    case 'fill-current': return { fill: 'currentColor' };
    case 'stroke-current': return { stroke: 'currentColor' };

    default: return {};
  }
}

// Test case 1: Always generating objects
function testAlwaysGenerate() {
  return cases.map(set =>
  	set.map((style) => composeStyle(style))
  )
}

// Test case 2: Using a cache
function testWithCache() {
  const cache = {};
  return cases.map(set =>
  	set.map(style => {
      if (!cache[style]) {
        cache[style] = composeStyle(style);
      }
      return cache[style];
    })
  )
}

const cases = []
for (let i = 0; i < iterations; i++) {
	cases.push(getRandomClasses(i % 15))
}
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
Always Create
testAlwaysGenerate()
ready
Always Cache
testWithCache()
ready
Always Create and Spread
testAlwaysGenerate()
	.map(v => Object.assign({}, ...v))
ready
Always Cache and Spread
testWithCache()
	.map(v => Object.assign({}, ...v))
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.