Skip to main content

Overview

ONNX Runtime provides flexible threading options to optimize performance on multi-core systems. This guide covers thread pool configuration, intra-op and inter-op parallelism, and best practices for concurrent execution.

Threading Architecture

ONNX Runtime supports two threading implementations:
  1. ORT Thread Pool: Custom thread pool implementation (default)
  2. OpenMP: Industry-standard parallel programming framework (opt-in at build time)
The choice is determined at build time using the --use_openmp flag.

Thread Pool Types

Intra-Op Thread Pool

Parallelism within a single operator:
Use cases:
  • Matrix multiplications
  • Convolution operations
  • Element-wise operations on large tensors

Inter-Op Thread Pool

Parallelism between independent operators:
Use cases:
  • Models with parallel branches
  • Independent operations in the graph
  • Pipeline parallelism

Configuration Examples

CPU-Bound Workloads

Models with Parallel Branches

High-Throughput Server

Execution Modes

Sequential Execution

Characteristics:
  • Lower scheduling overhead
  • Operators execute one at a time
  • Better for simple, linear graphs
  • Default mode for most scenarios

Parallel Execution

Characteristics:
  • Higher parallelism between operators
  • Better for complex graphs with independent paths
  • Higher scheduling overhead
  • Requires inter-op thread pool

C++ API

Basic Configuration

Custom Thread Pool

Threading Abstractions for Op Developers

ONNX Runtime provides abstractions for implementing parallel operators:

TryParallelFor

TrySimpleParallelFor

Simplified version for uniform work:

TryBatchParallelFor

For batched operations:

ShouldParallelize

Check if parallelization is beneficial:

DegreeOfParallelism

Get available parallelism:

ParallelSection

Group multiple loops in a single parallel section:
This amortizes thread pool entry/exit costs.

OpenMP vs ORT Thread Pool

Building with OpenMP

When to Use OpenMP

Advantages:
  • Industry-standard parallelization
  • Mature optimization
  • Good for CPU-intensive ops
Considerations:
  • May conflict with application-level OpenMP
  • Less control over thread pool
  • Build-time decision

When to Use ORT Thread Pool

Advantages:
  • Full control over threading
  • No conflicts with application threads
  • Consistent behavior across platforms
  • Runtime configuration
Use cases:
  • Custom threading requirements
  • Embedding in existing applications
  • Fine-grained control needed

Best Practices

1. Match Thread Count to Hardware

2. Avoid Over-subscription

3. Start with Sequential Mode

4. Tune for Your Workload

5. Set Environment Variables

Control system-level threading:

6. Concurrent Inference

For concurrent requests, limit per-session threads:

Platform-Specific Considerations

Linux

Windows

macOS

Troubleshooting

Poor CPU Utilization

Symptoms: Low CPU usage during inference Solutions:
  1. Increase intra-op threads
  2. Enable parallel execution mode
  3. Check for I/O bottlenecks

Thread Contention

Symptoms: Performance degrades with more threads Solutions:
  1. Reduce thread count
  2. Use sequential execution
  3. Profile for lock contention

Inconsistent Performance

Symptoms: High latency variance Solutions:
  1. Fix thread count (don’t use default)
  2. Disable dynamic threading
  3. Pin to physical cores

Important Guidelines for Developers

Do not use #ifdef _OPENMP or #pragma omp directly in operator code.Always use the threading abstractions provided in:
  • threadpool.h - ThreadPool class
  • thread_utils.h - Threading utility functions
These abstractions handle both OpenMP and non-OpenMP builds automatically.

Example: Correct Approach

Example: Incorrect Approach

Performance Tuning Examples

Example 1: Latency-Optimized

Example 2: Throughput-Optimized

Example 3: Server Deployment

See Also