OpenAPI Generator Performance — Optimizing Code Generation Speed and Output Quality
In this tutorial, you will learn about OpenAPI Generator Performance. We cover key concepts, practical examples, and best practices to help you master this topic.
OpenAPI Generator performance focuses on reducing generation time through incremental builds, parallel generation, batch processing, and optimizing output to minimize code size for large API specifications.
What You'll Learn
- Measuring and profiling generation time
- Incremental generation for faster iterations
- Parallel generation across languages
- Batch processing multiple API specs
- Reducing generated code footprint
Why It Matters
Generation time impacts developer workflow. A spec with 500 endpoints generating 10 language SDKs can take 5+ minutes. DodaTech's generator processes 15 internal API specs across 8 languages daily, and optimization reduced generation time from 12 minutes to 90 seconds.
Real-World Use
A platform with 15 Microservices, each with its own OpenAPI spec, generates SDKs daily. Incremental generation detects unchanged specs and skips regeneration. Parallel processing generates all 8 language SDKs simultaneously. A full pipeline that took 12 minutes now completes in 90 seconds.
flowchart TD
A["15 API Specs"] --> B{"Spec changed?"}
B -->|"No"| C["Skip generation"]
B -->|"Yes"| D["Queue for generation"]
D --> E["Parallel Group 1"]
D --> F["Parallel Group 2"]
D --> G["Parallel Group 3"]
E --> H["Python SDK"]
E --> I["TypeScript SDK"]
F --> J["Java SDK"]
F --> K["Go SDK"]
G --> L["C# SDK"]
G --> M["Ruby SDK"]
H --> N["Total: 90 seconds"]
I --> N
J --> N
K --> N
Code Examples
Example 1: Profiling Generation Time
import time
import subprocess
import json
from pathlib import Path
def profile_generation(spec_file, generators, output_base='/tmp/sdks'):
"""Profile generation time for each generator."""
results = {}
for gen in generators:
output_dir = f"{output_base}/{gen}"
start = time.time()
subprocess.run([
'openapi-generator-cli', 'generate',
'-i', spec_file,
'-g', gen,
'-o', output_dir,
'--skip-overwrite'
], capture_output=True)
elapsed = time.time() - start
file_count = len(list(Path(output_dir).rglob('*')))
total_size = sum(f.stat().st_size for f in Path(output_dir).rglob('*') if f.is_file())
results[gen] = {
'time_seconds': round(elapsed, 2),
'file_count': file_count,
'total_size_kb': round(total_size / 1024, 1)
}
print(json.dumps(results, indent=2))
return results
# Profile all target generators
results = profile_generation(
'openapi.yaml',
['python', 'typescript-axios', 'java', 'go', 'csharp-netcore']
)
# Output:
# {
# "python": {"time_seconds": 3.2, "file_count": 45, "total_size_kb": 234.0},
# "typescript-axios": {"time_seconds": 4.5, "file_count": 38, "total_size_kb": 189.0},
# ...
# }
Example 2: Incremental Generation
import hashlib
import json
from pathlib import Path
import subprocess
class IncrementalGenerator:
def __init__(self, cache_file='/tmp/gen_cache.json'):
self.cache_file = Path(cache_file)
self.cache = self._load_cache()
def _load_cache(self):
if self.cache_file.exists():
return json.loads(self.cache_file.read_text())
return {}
def _save_cache(self):
self.cache_file.write_text(json.dumps(self.cache, indent=2))
def _spec_hash(self, spec_file):
"""Compute hash of spec file contents."""
return hashlib.sha256(Path(spec_file).read_bytes()).hexdigest()
def needs_generation(self, spec_file, generator):
"""Check if regeneration is needed."""
current_hash = self._spec_hash(spec_file)
cache_key = f"{spec_file}:{generator}"
cached_hash = self.cache.get(cache_key)
if cached_hash == current_hash:
print(f"SKIP {generator}: spec unchanged")
return False
print(f"GENERATE {generator}: spec changed")
return True
def mark_generated(self, spec_file, generator):
"""Mark spec as generated for this generator."""
cache_key = f"{spec_file}:{generator}"
self.cache[cache_key] = self._spec_hash(spec_file)
self._save_cache()
def generate(self, spec_file, generators, output_base='/tmp/sdks'):
"""Generate only changed specs."""
for gen in generators:
if self.needs_generation(spec_file, gen):
output_dir = f"{output_base}/{gen}"
subprocess.run([
'openapi-generator-cli', 'generate',
'-i', spec_file,
'-g', gen,
'-o', output_dir,
'--skip-overwrite'
], check=True)
self.mark_generated(spec_file, gen)
# Usage
gen = IncrementalGenerator()
gen.generate('openapi.yaml', ['python', 'typescript', 'java'])
# First run: generates all three
# Second run (no spec change): skips all three
Example 3: Batch Parallel Generation
import concurrent.futures
import subprocess
import time
def generate_sdk(spec_file, generator, output_base):
"""Generate SDK for a single generator."""
output_dir = f"{output_base}/{generator}"
start = time.time()
result = subprocess.run([
'openapi-generator-cli', 'generate',
'-i', spec_file,
'-g', generator,
'-o', output_dir
], capture_output=True, text=True)
elapsed = time.time() - start
return {
'generator': generator,
'success': result.returncode == 0,
'time': round(elapsed, 2),
'error': result.stderr[:200] if result.returncode != 0 else None
}
def batch_generate(spec_file, generators, output_base='/tmp/sdks',
max_workers=4):
"""Generate SDKs in parallel with worker limit."""
print(f"Generating {len(generators)} SDKs with {max_workers} workers")
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [
executor.submit(generate_sdk, spec_file, gen, output_base)
for gen in generators
]
results = []
for future in concurrent.futures.as_completed(futures):
result = future.result()
status = "PASS" if result['success'] else "FAIL"
print(f" [{status}] {result['generator']}: {result['time']}s")
results.append(result)
total_time = sum(r['time'] for r in results)
wall_time = time.time() - start_time
print(f"\nTotal CPU time: {total_time}s")
print(f"Wall clock time: {wall_time:.2f}s")
return results
# Parallel batch generation
start_time = time.time()
results = batch_generate(
'openapi.yaml',
['python', 'typescript-axios', 'java', 'go',
'csharp-netcore', 'ruby', 'php', 'swift5'],
max_workers=4
)
Common Mistakes
1. Full Generation on Every Change
Use incremental generation. Most spec changes affect only a few endpoints.
2. Sequential Generation for Multiple Languages
Always parallelize language generation. Languages are independent of each other.
3. Generating Unused Generators
Only generate the languages your teams actually use. Each unused generator wastes time.
4. Ignoring Generator-Specific Options
Some generators are faster with specific flags. Research and benchmark generator options.
5. Not Caching Generator Outputs
Cache generated code across CI runs. Only regenerate when the spec or generator version changes.
Practice Questions
- How does incremental generation improve performance?
- When should you parallelize generation?
- What affects generation time the most?
- How do you measure generation performance?
- What strategies reduce generated code size?
Answers:
- It skips regeneration for specs that haven't changed, saving time in CI/CD pipelines with multiple specs.
- When generating SDKs for multiple languages — each language is independent and can run in parallel.
- Spec size (number of endpoints and schemas), generator complexity, and output filesystem I/O.
- Time each generator, count output files, and measure total output size.
- Use
--additional-properties=skipDefaultInterface=true, remove unused models, and trim schema descriptions.
Challenge: Build a batch generation pipeline that processes 5 API specs, generates 6 language SDKs each, uses incremental caching and parallel workers. Profile and optimize to complete in under 60 seconds.
FAQ
What's Next
Optimize your {{< ilink "OpenAPI" "CI/CD Code Generation with OpenAPI Generator" }} pipeline, and learn {{< ilink "OpenAPI" "OpenAPI Generator Customization" }} to reduce unnecessary output.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro