Performance Optimization Strategies – Building Responsive Device Applications

Performance in KOS applications focuses on responsive user interfaces and efficient reactive updates rather than network optimization. Since KOS deployments typically run with co-located browser and backend (on the same device or local network), traditional web performance concerns like network latency, connection pooling, and bandwidth optimization are largely irrelevant.

Understanding KOS Performance Characteristics

KOS applications face unique performance challenges centered around:

  • High-frequency device events – Sensors and hardware components can generate rapid data streams
  • Reactive model updates – Changes must propagate efficiently through model hierarchies
  • UI responsiveness – Interfaces must remain responsive during intensive device operations
  • Memory efficiency – Applications run for extended periods on embedded devices

The key insight is that network performance is not a concern in typical KOS deployments. Instead, optimization focuses on managing reactive updates, controlling event flow, and maintaining smooth user experiences during device operations.

Model Update Optimization

The foundation of KOS performance optimization lies in understanding how model updates propagate through the reactive system. When device events trigger model property changes, the framework automatically updates dependent models and user interface components. Optimizing this process requires strategic thinking about update frequency, dependency relationships, and batching strategies.

High-frequency device events require careful flow control to maintain UI responsiveness. KOS provides built-in flow control mechanisms that handle this elegantly:

const PROP_SENSOR_ID = createPropKey<HighFrequencySensorImpl>("sensorId");
@kosModel({ modelTypeId: "high-frequency-sensor", singleton: false })
export class HighFrequencySensorImpl implements IKosDataModel {
  sensorId!: string;
  latestReading?: SensorReading;
  averageAmplitude?: number;
  peakAmplitude?: number;
  @kosTopicHandler({
    topic: `/sensors/${PROP_SENSOR_ID}/vibration-data`,
    websocket: true,
    flow: {
      // Throttle to 10Hz for UI updates while preserving all data
      throttle: 100, // milliseconds
      // Transform raw readings into UI-friendly aggregates
      transform: (readings: SensorReading[]) => {
        const amplitudes = readings.map(r => r.amplitude);
        return {
          latest: readings[readings.length - 1],
          average: amplitudes.reduce((a, b) => a + b, 0) / amplitudes.length,
          peak: Math.max(...amplitudes)
        };
      }
    }
  })
  handleAggregatedReadings(data: { latest: SensorReading, average: number, peak: number }): void {
    this.latestReading = data.latest;
    this.averageAmplitude = data.average;
    this.peakAmplitude = data.peak;
    // KOS handles all the throttling and aggregation automatically
  }
}

This approach maintains data accuracy for device operations while preventing user interface performance degradation from excessive update frequencies.

Dependency Graph Optimization

Model relationships create dependency graphs that determine how changes propagate through the system. Complex dependency structures can create performance bottlenecks when changes trigger cascading updates across multiple models. Optimizing these relationships requires understanding the difference between necessary and unnecessary dependencies:

@kosModel({ modelTypeId: "system-coordinator", singleton: true })
export class SystemCoordinatorImpl implements IKosDataModel {
  @kosChild diagnostics?: DiagnosticsModel;
  @kosChild analytics?: AnalyticsModel;
  @kosChild safety?: SafetyModel; // Critical - load immediately
  // Optimize dependencies with specific observation patterns
  @kosModelEffect({
    dependencies: (model) => [
      model.safety?.criticalAlerts, // Only watch critical alerts
      model.currentOperationMode     // Only watch mode changes
    ],
  })
  handleCriticalSystemChanges(): void {
    // React only to critical changes that require immediate action
    if (this.safety?.criticalAlerts?.length > 0) {
      this.initiateEmergencyProtocol();
    }
    if (this.currentOperationMode === "maintenance") {
      this.enableDiagnosticLogging();
    }
  }
  // Separate effect for non-critical updates
  @kosModelEffect({
    dependencies: (model) => [model.diagnostics?.systemHealth],
  })
  handleNonCriticalUpdates(): void {
    // Handle updates that can be processed with lower priority
    if (this.diagnostics?.systemHealth) {
      this.scheduleBackgroundAnalysis();
    }
  }
}

By creating targeted dependencies that observe only necessary properties, the system avoids unnecessary update cascades while maintaining responsiveness to critical changes.

Event Processing Efficiency

WebSocket event processing forms a significant part of KOS application performance. Optimizing event handlers involves reducing processing time per event, implementing efficient data transformation patterns, and avoiding synchronous operations that block the event processing thread:

const PROP_LINE_ID = createPropKey<ProductionLineImpl>("lineId");
@kosModel({ modelTypeId: "production-line", singleton: false })
export class ProductionLineImpl implements IKosDataModel {
  lineId!: string;
  criticalEventCount: number = 0;
  normalEventCount: number = 0;
  @kosTopicHandler({
    topic: `/production/line/${PROP_LINE_ID}/events`,
    websocket: true,
    flow: {
      // Batch events for efficient processing
      batch: { window: 10, size: 100 }, // 10ms window or 100 events max
      // Transform batched events into prioritized groups
      transform: (events: ProductionEvent[]) => ({
        critical: events.filter(e => e.priority === "critical"),
        normal: events.filter(e => e.priority === "normal")
      }),
      // Handle backpressure if processing is slow
      backpressure: { maxConcurrent: 2, maxQueue: 1000 }
    }
  })
  handleBatchedEvents(eventGroups: { critical: ProductionEvent[], normal: ProductionEvent[] }): void {
    // Process critical events first
    this.criticalEventCount += eventGroups.critical.length;
    // Process normal events
    this.normalEventCount += eventGroups.normal.length;
    // KOS handles the batching, throttling, and error handling automatically
    this.logger.debug(`Processed ${eventGroups.critical.length} critical, ${eventGroups.normal.length} normal events`);
  }
}

This batching approach reduces the overhead of individual event processing while maintaining responsiveness to time-critical events.

Memory Management and Model Lifecycle

KOS models should focus on reflecting current device state, not managing data persistence. The backend handles historical data storage and cleanup. Models need proper lifecycle management to prevent memory leaks:

const PROP_SENSOR_ID = createPropKey<SensorMonitorImpl>("sensorId");
@kosModel({ modelTypeId: "sensor-monitor", singleton: false })
export class SensorMonitorImpl implements IKosDataModel {
  sensorId!: string;
  currentValue?: number;
  lastUpdate?: Date;
  status: 'online' | 'offline' | 'error' = 'offline';
  // Models reflect current state, not historical data
  @kosTopicHandler({
    topic: `/sensors/${PROP_SENSOR_ID}/current`,
    websocket: true
  })
  handleCurrentReading(data: SensorReading): void {
    this.currentValue = data.value;
    this.lastUpdate = new Date(data.timestamp);
    this.status = 'online';
  }
  @kosTopicHandler({
    topic: `/sensors/${PROP_SENSOR_ID}/status`,
    websocket: true
  })
  handleStatusChange(status: { status: 'online' | 'offline' | 'error' }): void {
    this.status = status.status;
  }
  // Models don't manage persistence - backend handles historical data
  // Current device state is the model's responsibility
}

Models maintain only current device state. The framework automatically handles cleanup of subscriptions and model lifecycle.

Container Capacity Management for Memory Safety

Container models provide automatic capacity management as a safety net to prevent unbounded memory growth in event-driven scenarios. This feature is particularly important for long-running embedded applications where memory leaks can cause system instability over time.

The Safety Net Philosophy

Capacity management operates as a last line of defense, not a primary cleanup mechanism:

interface EventLogContainerImpl extends KosContainerAware<LogEntryModel> {}
@kosModel({ modelTypeId: "event-log-container", singleton: true })
@kosContainerAware<LogEntryModel>({
  containerOptions: {
    // SAFETY NET: Prevent unbounded growth from missing cleanup handlers
    maxCapacity: 1000,
    evictionStrategy: 'fifo',  // Remove oldest entries first
    evictionBatchSize: 100     // Evict 100 entries when capacity exceeded
  }
})
export class EventLogContainerImpl implements IKosDataModel {
  id: string;
  constructor(modelId: string, options: ContainerOptions, context: KosCreationContext) {
    this.id = modelId;
  }
  @kosTopicHandler({
    topic: "/device/events",
    websocket: true
  })
  handleDeviceEvent(event: DeviceEvent): void {
    const logEntry = LogEntryModel.instance(event.id)
      .options(event)
      .build();
    this.addModel(logEntry);
    // Primary cleanup would happen here (e.g., explicit removal after ACK)
    // Capacity management only activates if primary cleanup fails
  }
}

Choosing the Right Eviction Strategy

FIFO (First-In-First-Out) – Best for event streams and logs:

// Event logs where older events are less relevant
maxCapacity: 1000,
evictionStrategy: 'fifo'

LRU (Least Recently Used) – Best for caches and accessed collections:

// Data caches where access patterns indicate importance
maxCapacity: 500,
evictionStrategy: 'lru'

Custom Strategy – Best for domain-specific retention rules:

// Custom logic for prioritized eviction
maxCapacity: 200,
evictionStrategy: 'custom',
customEvictionFilter: (models: TroubleModel[]) => {
  // Evict resolved troubles first
  const resolved = models.filter(t => t.isResolved);
  const unresolved = models.filter(t => !t.isResolved);
  return [...resolved.sort((a, b) => a.age - b.age), ...unresolved];
}

Monitoring Capacity Evictions

Capacity evictions are logged at WARNING level, indicating a potential issue:

// Capacity exceeded warning
log.warn(
  `Container capacity exceeded (1005/1000). ` +
  `Evicting 100 models using fifo strategy. ` +
  `This may indicate missing cleanup handlers or data inconsistency.`
);
// Per-model eviction logging
log.warn(`Evicting model: event-12345 (type: LogEntryModel)`);

When to investigate capacity evictions:

  • Frequent evictions indicate cleanup handlers aren’t working
  • Growing eviction frequency suggests data lifecycle issues
  • Capacity warnings should trigger operational alerts for investigation

Performance Impact

Capacity management has minimal performance impact during normal operation:

  • Insertion tracking: O(1) – adds model ID to insertion order array
  • Access tracking (LRU): O(1) – updates access timestamp in map
  • Eviction: O(n log n) for sorting, then O(k) for k evictions
  • Batch eviction: Processes multiple models efficiently in one cycle

The performance cost is negligible compared to unbounded memory growth causing system crashes.

React Integration Performance

The integration between KOS models and React components requires careful attention to prevent unnecessary re-renders and maintain responsive user interfaces. React performance optimization in KOS applications focuses on minimizing render cycles while preserving reactivity to critical model changes:

// Model handles all business logic and computed values
@kosModel({ modelTypeId: "sensor-dashboard", singleton: true })
export class SensorDashboardModelImpl implements IKosDataModel {
  @kosChild sensorContainer!: SensorContainerModel;
  // Computed properties handled by the model
  get criticalSensors(): SensorModel[] {
    return this.sensorContainer.items.filter(sensor => sensor.status === "critical");
  }
  get averageTemperature(): number {
    const validTemps = this.sensorContainer.items
      .map(s => s.temperature)
      .filter((temp): temp is number => temp !== undefined);
    return validTemps.length > 0
      ? validTemps.reduce((a, b) => a + b) / validTemps.length
      : 0;
  }
  get hasCriticalAlerts(): boolean {
    return this.criticalSensors.length > 0;
  }
}
// Component is just presentation - no business logic
const SensorDashboard: React.FC = () => {
  const dashboard = useKosModel(SensorDashboardModel);
  return (
    <div className="sensor-dashboard">
      <SummaryCard
        title="Average Temperature"
        value={dashboard.averageTemperature.toFixed(1)}
        unit="°F"
      />
      {dashboard.hasCriticalAlerts && (
        <CriticalAlerts sensors={dashboard.criticalSensors} />
      )}
      <SensorGrid sensors={dashboard.sensorContainer.items} />
    </div>
  );
};

This optimization approach maintains responsive interfaces while preventing performance degradation from excessive re-rendering.

Device Communication Optimization

KOS handles all WebSocket connection management automatically. Models focus on device logic, not communication infrastructure. Performance optimization involves proper use of KOS’s built-in capabilities:

const PROP_DEVICE_ID = createPropKey<DeviceControllerImpl>("deviceId");
@kosModel({ modelTypeId: "device-controller", singleton: false })
export class DeviceControllerImpl implements IKosDataModel {
  deviceId!: string;
  status: 'online' | 'offline' = 'offline';
  lastCommand?: string;
  // KOS handles all WebSocket communication automatically
  @kosTopicHandler({
    topic: `/device/${PROP_DEVICE_ID}/status`,
    websocket: true
  })
  handleStatusUpdate(status: DeviceStatus): void {
    this.status = status.online ? 'online' : 'offline';
  }
  // Models send commands - KOS handles delivery, retry, and connection management
  @kosFuture
  async executeCommand(command: DeviceCommand): Promise<CommandResult> {
    this.lastCommand = command.type;
    // KOS framework handles connection pooling, retry logic, and error handling
    return this.sendCommand('/device/execute', command);
  }
  // Models focus on business logic, not communication details
  get isReady(): boolean {
    return this.status === 'online';
  }
}

KOS automatically optimizes communication – models don’t manage connections or implement retry logic.

Performance Monitoring and Best Practices

KOS provides built-in performance monitoring and handles resource management automatically. Models should focus on device representation, not system-level performance monitoring:

const PROP_DEVICE_ID = createPropKey<DevicePerformanceImpl>("deviceId");
@kosModel({ modelTypeId: "device-performance", singleton: false })
export class DevicePerformanceImpl implements IKosDataModel {
  deviceId!: string;
  cpuUsage?: number;
  memoryUsage?: number;
  responseTime?: number;
  // Models reflect device performance metrics, not application performance
  @kosTopicHandler({
    topic: `/device/${PROP_DEVICE_ID}/performance`,
    websocket: true
  })
  handlePerformanceUpdate(metrics: DeviceMetrics): void {
    this.cpuUsage = metrics.cpuUsage;
    this.memoryUsage = metrics.memoryUsage;
    this.responseTime = metrics.responseTime;
  }
  // Computed properties for device health assessment
  get isPerformingWell(): boolean {
    return (this.cpuUsage ?? 0) < 80 &&
           (this.memoryUsage ?? 0) < 85 &&
           (this.responseTime ?? 0) < 1000;
  }
  get performanceStatus(): 'excellent' | 'good' | 'poor' {
    if (!this.isPerformingWell) return 'poor';
    return this.responseTime && this.responseTime < 500 ? 'excellent' : 'good';
  }
}

This monitoring approach provides visibility into application performance characteristics while enabling proactive optimization based on real operational data.

Deployment and Production Optimization

KOS applications run locally on embedded devices with co-located browser and backend, eliminating traditional web performance concerns:

What KOS Applications Don’t Need:

  • Network retry mechanisms (backend is local)
  • Data compression (no network transmission)
  • Connection pooling (single local connection)
  • Bandwidth optimization (local communication)
  • Cache invalidation strategies (real-time local updates)

What KOS Applications Do Need:

  • Efficient reactive model updates for responsive UI
  • Flow control for high-frequency device events
  • Proper model hierarchy design to minimize update cascades
  • Memory-conscious patterns for long-running embedded applications

Performance optimization in KOS focuses on the user experience: keeping interfaces responsive during device operations, managing high-frequency sensor data efficiently, and ensuring smooth animations and transitions while device hardware performs complex operations.

The KOS framework handles all communication infrastructure automatically, allowing developers to focus on device logic and user interface responsiveness rather than network or system-level optimization.