โšก Performance Benchmarks

Head-to-head performance comparison with other DI frameworks

Real-time Tests 1000 Runs Each Multiple Metrics Interactive Charts

๐Ÿ“‹ About These Benchmarks

These benchmarks compare NovaDI with other popular TypeScript DI frameworks across multiple performance metrics. Each test runs 1000 iterations to ensure accurate results. Open your browser console (F12) to see detailed execution logs and timing information.

๐Ÿ“Š Test Metrics:

  • Singleton Resolution: Measure cached singleton lookup performance
  • Transient Resolution: Test new instance creation on every resolve
  • Build Time: Container setup time with 100 registrations
  • Complex Graph: Realistic multi-layer dependency graph resolution
  • Bundle Size: Minified + gzipped framework size

๐ŸŽฏ Compared Frameworks: NovaDI, Brandi, InversifyJS, TSyringe, TypeDI

๐Ÿ“š Looking for usage examples? Check out the Examples page for interactive demos of NovaDI features and patterns.

๐Ÿงช Test Class Structures

Below are the class structures used in the performance benchmarks. Understanding what's being tested helps interpret the results.

๐Ÿ“ฆ Simple Test Classes (Singleton & Transient Tests)

These minimal classes are used to benchmark pure resolution speed without complexity.

// Simple test interfaces
interface ICache {
  get(key: string): any
  set(key: string, value: any): void
}

// Simple implementations (no dependencies)
class Logger implements ILogger {
  log(message: string, context?: string): void {}
  info(message: string, context?: string): void {}
  warn(message: string, context?: string): void {}
  error(message: string, error?: Error): void {}
}

class Cache implements ICache {
  private data = new Map()
  get(key: string) { return this.data.get(key) }
  set(key: string, value: any) { this.data.set(key, value) }
}

// Benchmark code
builder.registerType(Logger).as<ILogger>().singleInstance()
builder.registerType(Cache).as<ICache>().singleInstance()

// Measure 1000 cached singleton resolutions
for (let i = 0; i < 1000; i++) {
  app.resolveType<ILogger>()
  app.resolveType<ICache>()
}

๐Ÿค– Complex Graph Test (Smart Home System)

This test uses the full smart home system (Example 6) with multi-layer dependencies. Same structure is used for No AutoWire variant.

// Multi-layer dependency graph:
// AutomationService โ†’ Logger + EventBus โ†’ Logger
// Devices (SmartThermostat, SmartLight) โ†’ Logger
// Sensors (TemperatureSensor, MotionSensor) - no dependencies

// Core services
builder.registerType(ConsoleLogger).as<ILogger>().singleInstance()
builder
  .registerType(EventBus)
  .as<IEventBus>()
  .autoWire({ map: { logger: (c) => c.resolveType<ILogger>() } })
  .singleInstance()

builder
  .registerType(AutomationService)
  .as<AutomationService>()
  .autoWire({
    map: {
      logger: (c) => c.resolveType<ILogger>(),
      eventBus: (c) => c.resolveType<IEventBus>()
    }
  })
  .singleInstance()

// Sensors (instances)
builder.registerInstance(new TemperatureSensor(...)).as<ISensor>().keyed('TempSensor')
builder.registerInstance(new MotionSensor(...)).as<ISensor>().keyed('MotionSensor')

// Devices (with autowired logger dependency)
builder.registerType(SmartThermostat).as<IDevice>().keyed('Thermostat')
  .autoWire({ map: { logger: (c) => c.resolveType<ILogger>(), ... } })

// Measure 1000 complex resolutions
for (let i = 0; i < 1000; i++) {
  app.resolveType<AutomationService>()  // Resolves Logger + EventBus chain
}

โšก Same Structure, No AutoWire

Uses direct factory registration instead of .autoWire() to eliminate function call overhead.

// Instead of .autoWire({ map: { logger: (c) => c.resolveType<ILogger>() } })
// Use direct factory:

builder.register((c) => {
  const logger = c.resolveType<ILogger>()
  return new EventBus(logger)
}).as<IEventBus>().singleInstance()

builder.register((c) => {
  const logger = c.resolveType<ILogger>()
  const eventBus = c.resolveType<IEventBus>()
  return new AutomationService(logger, eventBus)
}).as<AutomationService>().singleInstance()

// Same test: 1000 resolutions of AutomationService
// Expected result: Faster due to no autoWire map overhead

๐Ÿ“š Technical Reference - Smart Home Domain Classes

โ–ผ

Interfaces

interface ILogger
log(message: string, context?: string): void
info(message: string, context?: string): void
warn(message: string, context?: string): void
error(message: string, error?: Error): void
interface IEventBus
publish(event: string, data: any): void
subscribe(event: string, handler: Function): void
interface ISensor
id: string
name: string
roomId: string
getValue(): number
getUnit(): string
interface IDevice
id: string
name: string
roomId: string
isOn: boolean
turnOn(): void
turnOff(): void
getStatus(): string

Service Classes

class ConsoleLogger
implements ILogger
constructor()
No dependencies
class EventBus
implements IEventBus
constructor(logger: ILogger)
โ†’ ILogger
class AutomationService
constructor(
logger: ILogger,
eventBus: IEventBus
)
โ†’ ILogger
โ†’ IEventBus

Sensor Classes

class TemperatureSensor
implements ISensor
constructor(
id: string,
name: string,
roomId: string,
currentTemp: number = 22
)
No injectable dependencies
class MotionSensor
implements ISensor
constructor(
id: string,
name: string,
roomId: string
)
No injectable dependencies

Device Classes (Actuators)

class SmartThermostat
implements IDevice
constructor(
id: string,
name: string,
roomId: string,
logger: ILogger
)
โ†’ ILogger
class SmartLight
implements IDevice
constructor(
id: string,
name: string,
roomId: string,
logger: ILogger
)
โ†’ ILogger

Dependency Graph (Demo 5 / Performance Tests)

Singleton Instance โ†’ Depends on
AutomationService
AutomationService
โ†’ ILogger, IEventBus
ConsoleLogger
ILogger
EventBus
IEventBus
โ†’ ILogger
Additional registered services (not directly injected into AutomationService)
TemperatureSensor
ISensor (keyed: 'TempSensor')
MotionSensor
ISensor (keyed: 'MotionSensor')
SmartThermostat
IDevice (keyed: 'Thermostat')
โ†’ ILogger
SmartLight
IDevice (keyed: 'Light')
โ†’ ILogger
Resolution Order: When resolving AutomationService, the container first resolves ILogger (singleton, cached), then EventBus (singleton, cached, uses cached ILogger), then AutomationService (singleton, uses cached ILogger and EventBus). Devices and sensors are registered but not directly injected into AutomationService.

โšก Performance Benchmarks

โ–ผ

Head-to-head performance comparison with other popular DI frameworks

  • NovaDI - Our decorator-free, browser-first framework
  • Brandi - Decorator-free TypeScript DI
  • InversifyJS - Popular decorator-based DI framework
  • TSyringe - Microsoft's lightweight DI container
  • TypeDI - TypeStack's decorator-based container
  • 4 metrics: Resolution Speed, Build Time, Complex Graph, Bundle Size
pending

๐Ÿ“Š Performance Benchmark Results

๐Ÿ”ฌ NovaDI Complex Graph (No AutoWire)

โ–ผ

Testing NovaDI Complex Graph performance without AutoWire overhead - direct factory registration

  • Direct Factory Registration - No autowire map function overhead
  • Fair Comparison - Same pattern as TypeDI implementation
  • Expected Performance - Should match or beat TypeDI
  • 1000 Resolutions - Complex dependency graph test
pending
Run demo to see performance results

โš ๏ธ Console Output Required

Press F12 or Ctrl+Shift+I (Windows/Linux) / Cmd+Option+I (Mac) to open your browser's developer console. All demo execution details, dependency resolution logs, and automation events are logged there with grouped output for easy navigation.