CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-threadpoolctl

Python helpers to limit the number of threads used in threadpool-backed native libraries for scientific computing

Overview
Eval results
Files

advanced-control.mddocs/

Advanced Thread Pool Control

Programmatic control of thread pools with library selection, filtering, and advanced limiting patterns. The ThreadpoolController provides fine-grained control over individual libraries and their thread settings.

Capabilities

ThreadpoolController

Main controller class that discovers and manages all loaded thread pool libraries.

class ThreadpoolController:
    """
    Controller for all loaded supported thread pool libraries.
    
    Automatically discovers supported libraries when instantiated and provides
    methods for filtering, selecting, and controlling their thread settings.
    
    Attributes:
        lib_controllers: list[LibController] 
            List of individual library controller instances
    """
    
    def __init__(self):
        """
        Initialize controller and discover loaded libraries.
        
        Scans system for loaded shared libraries and creates controllers
        for supported thread pool libraries (BLAS and OpenMP implementations).
        """
        
    def info(self):
        """
        Get information about all controlled libraries.
        
        Returns:
            list[dict]: List of library info dicts (same format as threadpool_info())
        """
        
    def select(self, **kwargs):
        """
        Filter libraries by attributes.
        
        Args:
            **kwargs: Attribute filters where key is attribute name and value
                     is either a single value or list of acceptable values.
                     Common keys: user_api, internal_api, prefix, version, etc.
                     
        Returns:
            ThreadpoolController: New controller with filtered library subset
        """
        
    def limit(self, *, limits=None, user_api=None):
        """
        Create thread limiter for this controller's libraries.
        
        Args:
            limits: int | dict | str | None - Thread limit specification
            user_api: str | None - API type filter
            
        Returns:
            _ThreadpoolLimiter: Context manager for temporary limiting
        """
        
    def wrap(self, *, limits=None, user_api=None):
        """
        Create decorator for this controller's libraries.
        
        Args:
            limits: int | dict | str | None - Thread limit specification  
            user_api: str | None - API type filter
            
        Returns:
            Decorator function for thread limiting
        """
        
    def __len__(self):
        """
        Number of controlled libraries.
        
        Returns:
            int: Count of lib_controllers
        """

Library Selection and Filtering

from threadpoolctl import ThreadpoolController

# Create controller
controller = ThreadpoolController()
print(f"Found {len(controller)} thread pool libraries")

# Select by API type
blas_controller = controller.select(user_api='blas')
openmp_controller = controller.select(user_api='openmp')

# Select by implementation
openblas_controller = controller.select(internal_api='openblas')
mkl_controller = controller.select(internal_api='mkl')

# Select by multiple criteria (OR logic)
specific_controller = controller.select(
    internal_api=['openblas', 'mkl'],
    prefix='libmkl_rt'
)

# Select by custom attributes (for libraries that have them)
openmp_blas = controller.select(threading_layer='openmp')

Advanced Limiting with Controllers

from threadpoolctl import ThreadpoolController

controller = ThreadpoolController()

# Limit only BLAS libraries to 1 thread
blas_controller = controller.select(user_api='blas')
with blas_controller.limit(limits=1):
    # Only BLAS libraries limited, OpenMP unchanged
    result = numpy_computation()

# Different limits for different implementations
openblas_controller = controller.select(internal_api='openblas')  
mkl_controller = controller.select(internal_api='mkl')

with openblas_controller.limit(limits=1):
    with mkl_controller.limit(limits=2):
        # OpenBLAS: 1 thread, MKL: 2 threads
        result = mixed_blas_computation()

# Complex filtering and limiting
high_thread_controller = controller.select(
    **{lib.prefix: lib.num_threads for lib in controller.lib_controllers 
       if lib.num_threads > 4}
)
with high_thread_controller.limit(limits=2):
    # Only libraries with >4 threads are limited to 2
    result = computation()

Individual Library Controller Access

from threadpoolctl import ThreadpoolController

controller = ThreadpoolController()

# Access individual library controllers
for lib_controller in controller.lib_controllers:
    print(f"Library: {lib_controller.internal_api}")
    print(f"  Current threads: {lib_controller.num_threads}")
    print(f"  Prefix: {lib_controller.prefix}")
    print(f"  Version: {lib_controller.version}")
    
    # Get full info dict
    info = lib_controller.info()
    print(f"  Full info: {info}")

FlexiBLAS Backend Management

For FlexiBLAS libraries, additional backend switching capabilities are available:

# FlexiBLAS-specific method available on FlexiBLAS controller instances
def switch_backend(self, backend):
    """
    Switch the FlexiBLAS backend.
    
    Args:
        backend: str - Backend name or path to shared library.
                      If not loaded, will be loaded first.
                      
    Raises:
        RuntimeError: If backend loading or switching fails
    """
from threadpoolctl import ThreadpoolController

controller = ThreadpoolController()
flexiblas_controller = controller.select(internal_api='flexiblas')

if flexiblas_controller:
    flex_lib = flexiblas_controller.lib_controllers[0]  # Get first FlexiBLAS instance
    
    print(f"Available backends: {flex_lib.available_backends}")
    print(f"Loaded backends: {flex_lib.loaded_backends}")  
    print(f"Current backend: {flex_lib.current_backend}")
    
    # Switch to different backend
    if 'OPENBLAS' in flex_lib.available_backends:
        flex_lib.switch_backend('OPENBLAS')
        print(f"Switched to: {flex_lib.current_backend}")

Controller Composition Patterns

from threadpoolctl import ThreadpoolController

# Create specialized controllers for different use cases
def create_compute_controller():
    """Controller optimized for compute workloads."""
    controller = ThreadpoolController()
    return controller.select(user_api='blas')

def create_parallel_controller():
    """Controller for parallel processing workloads."""  
    controller = ThreadpoolController()
    return controller.select(user_api='openmp')

# Use in context
compute_controller = create_compute_controller()
parallel_controller = create_parallel_controller()

with compute_controller.limit(limits=1):
    with parallel_controller.limit(limits=4):
        # BLAS: 1 thread, OpenMP: 4 threads
        result = hybrid_computation()

Dynamic Library Management

from threadpoolctl import ThreadpoolController
import importlib

# Controller state can change as new libraries are loaded
initial_controller = ThreadpoolController()
print(f"Initial libraries: {len(initial_controller)}")

# Load library that brings in new thread pools
import some_blas_library

# Need new controller to detect newly loaded libraries  
updated_controller = ThreadpoolController()
print(f"After import: {len(updated_controller)}")

# Compare library sets
initial_libs = {lib.filepath for lib in initial_controller.lib_controllers}
updated_libs = {lib.filepath for lib in updated_controller.lib_controllers}
new_libs = updated_libs - initial_libs
print(f"New libraries: {list(new_libs)}")

Error Handling and Edge Cases

from threadpoolctl import ThreadpoolController

controller = ThreadpoolController()

# Handle empty selections
blis_controller = controller.select(internal_api='blis')
if len(blis_controller) == 0:
    print("No BLIS libraries found")
else:
    with blis_controller.limit(limits=1):
        result = computation()

# Robust library access
openblas_controllers = controller.select(internal_api='openblas')
if openblas_controllers:
    for lib in openblas_controllers.lib_controllers:
        try:
            # Some old OpenBLAS versions may not support all operations
            threads = lib.num_threads
            lib.set_num_threads(1)
            lib.set_num_threads(threads)  # Restore
        except Exception as e:
            print(f"Library {lib.prefix} error: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-threadpoolctl

docs

advanced-control.md

custom-libraries.md

index.md

introspection.md

limiting.md

tile.json