AgentSkillsCN

midnight-proofs:prover-optimization

在优化证明器性能、降低证明生成内存占用、在 Docker 或 Kubernetes 中配置证明器基础设施、实施并行证明生成,或诊断证明器瓶颈时使用。

SKILL.md
--- frontmatter
name: midnight-proofs:prover-optimization
description: Use when optimizing prover performance, reducing proof generation memory usage, configuring prover infrastructure in Docker or Kubernetes, implementing parallel proof generation, or diagnosing prover bottlenecks.

Prover Optimization

Tune ZK prover performance for production workloads through memory optimization, parallelization, and infrastructure configuration.

When to Use

  • Optimizing proof generation speed for high-throughput services
  • Reducing memory usage during proof generation
  • Configuring prover infrastructure (Docker, Kubernetes)
  • Implementing parallel proof generation with worker pools
  • Diagnosing and fixing prover performance bottlenecks

Key Concepts

Performance Factors

FactorImpactOptimization
Circuit complexityHighSimplify circuit if possible
Memory allocationHighTune JVM/Node memory settings
CPU coresMediumScale workers to available cores
Disk I/OLowUse SSD for circuit keys
NetworkLowCo-locate with dependent services

Memory Requirements

Proof generation is memory-intensive. Typical requirements:

Circuit ComplexityMemory (per proof)
Simple (< 1000 constraints)512MB - 1GB
Medium (1000-10000 constraints)1GB - 4GB
Complex (> 10000 constraints)4GB - 16GB

Throughput Estimation

code
Proofs/hour = (Cores / Proofs-per-Core) * 3600 / Avg-Proof-Time-Seconds

Example: 8 cores, 2 proofs per core, 15 second proof time:

code
(8 / 2) * 3600 / 15 = 960 proofs/hour

References

DocumentDescription
memory-tuning.mdMemory configuration and optimization
parallelization.mdWorker pools and concurrent proof generation

Examples

ExampleDescription
docker-config/Optimized Docker configuration
worker-pool/Multi-process worker pool

Quick Start

1. Basic Memory Configuration

typescript
import { createProver } from '@midnight-ntwrk/midnight-js-prover';

const prover = await createProver({
  circuitKeysPath: './circuit-keys',
  memoryLimit: 8192,  // 8GB
  threads: 4,         // Number of worker threads
});

2. Environment Variables

bash
# Node.js memory settings
export NODE_OPTIONS="--max-old-space-size=8192"

# Prover-specific settings
export PROVER_MEMORY_LIMIT=8192
export PROVER_THREADS=4
export PROVER_PRELOAD_CIRCUITS="transfer,mint,burn"

3. Docker Resource Limits

yaml
# docker-compose.yml
services:
  prover:
    image: prover-service:latest
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 8G
        reservations:
          cpus: '2'
          memory: 4G

Common Patterns

Circuit Key Preloading

typescript
// Preload at startup to avoid load-time latency
const prover = await createProver({
  circuitKeysPath: './circuit-keys',
  preloadCircuits: ['transfer', 'mint', 'burn'],
});

// Verify circuits are loaded
const status = await prover.getStatus();
console.log('Loaded circuits:', status.loadedCircuits);

Memory Monitoring

typescript
import v8 from 'v8';

function getMemoryUsage(): {
  heapUsed: number;
  heapTotal: number;
  external: number;
} {
  const stats = v8.getHeapStatistics();
  return {
    heapUsed: Math.round(stats.used_heap_size / 1024 / 1024),
    heapTotal: Math.round(stats.total_heap_size / 1024 / 1024),
    external: Math.round(stats.external_memory / 1024 / 1024),
  };
}

// Log memory before/after proof generation
console.log('Before:', getMemoryUsage());
await prover.prove(circuitId, witness);
console.log('After:', getMemoryUsage());

Backpressure Control

typescript
class BackpressureController {
  private activeProofs = 0;
  private maxConcurrent: number;
  private waitQueue: Array<() => void> = [];

  constructor(maxConcurrent: number) {
    this.maxConcurrent = maxConcurrent;
  }

  async acquire(): Promise<void> {
    if (this.activeProofs < this.maxConcurrent) {
      this.activeProofs++;
      return;
    }

    // Wait for a slot
    await new Promise<void>((resolve) => {
      this.waitQueue.push(resolve);
    });
    this.activeProofs++;
  }

  release(): void {
    this.activeProofs--;
    const next = this.waitQueue.shift();
    if (next) next();
  }

  async withBackpressure<T>(fn: () => Promise<T>): Promise<T> {
    await this.acquire();
    try {
      return await fn();
    } finally {
      this.release();
    }
  }
}

// Usage
const controller = new BackpressureController(4); // Max 4 concurrent proofs

await controller.withBackpressure(async () => {
  return await prover.prove(circuitId, witness);
});

Graceful Degradation

typescript
async function proveWithFallback(
  circuitId: string,
  witness: WitnessData
): Promise<Proof> {
  const memoryUsage = getMemoryUsage();

  // If memory is high, wait before starting new proof
  if (memoryUsage.heapUsed > 6000) {
    console.warn('High memory usage, waiting...');
    await new Promise((r) => setTimeout(r, 5000));

    // Force garbage collection if available
    if (global.gc) {
      global.gc();
    }
  }

  try {
    return await prover.prove(circuitId, witness);
  } catch (error) {
    if (error.message.includes('out of memory')) {
      // Retry with reduced concurrency
      console.warn('OOM error, retrying after GC...');
      if (global.gc) global.gc();
      await new Promise((r) => setTimeout(r, 2000));
      return await prover.prove(circuitId, witness);
    }
    throw error;
  }
}

Performance Metrics

typescript
import { Counter, Histogram, Gauge } from 'prom-client';

const proofDuration = new Histogram({
  name: 'proof_generation_duration_seconds',
  help: 'Proof generation duration',
  labelNames: ['circuit_id'],
  buckets: [1, 5, 10, 15, 30, 60],
});

const proofCounter = new Counter({
  name: 'proofs_generated_total',
  help: 'Total proofs generated',
  labelNames: ['circuit_id', 'status'],
});

const memoryGauge = new Gauge({
  name: 'prover_memory_usage_bytes',
  help: 'Current prover memory usage',
});

// Instrument proof generation
async function proveWithMetrics(
  circuitId: string,
  witness: WitnessData
): Promise<Proof> {
  const end = proofDuration.startTimer({ circuit_id: circuitId });
  memoryGauge.set(process.memoryUsage().heapUsed);

  try {
    const proof = await prover.prove(circuitId, witness);
    proofCounter.inc({ circuit_id: circuitId, status: 'success' });
    return proof;
  } catch (error) {
    proofCounter.inc({ circuit_id: circuitId, status: 'error' });
    throw error;
  } finally {
    end();
  }
}

Performance Considerations

ConcernMitigation
Memory fragmentationRestart workers periodically
CPU throttlingUse dedicated prover nodes
GC pausesTune GC settings, limit heap size
Circuit load timePreload circuits at startup

Related Skills

  • proof-generation - Server-side proof generation
  • proof-caching - Cache proofs to reduce generation load
  • proof-verification - Verify generated proofs

Related Commands

None currently defined.