or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-control.mdcustom-libraries.mdindex.mdintrospection.mdlimiting.md

limiting.mddocs/

0

# Thread Limiting

1

2

Temporarily limit the number of threads used by thread pool libraries using context managers or decorators. This capability is essential for preventing oversubscription in workloads with nested parallelism.

3

4

## Capabilities

5

6

### Context Manager Interface

7

8

The primary interface for temporarily limiting thread pools.

9

10

```python { .api }

11

class threadpool_limits:

12

"""

13

Context manager and decorator for limiting thread pool sizes.

14

15

Can be used as:

16

1. Context manager: with threadpool_limits(limits=1): ...

17

2. Direct call: limiter = threadpool_limits(limits=1)

18

3. Decorator: @threadpool_limits(limits=1)

19

20

Parameters:

21

limits: int | dict | str | None

22

Thread limit specification:

23

- int: Set global limit for all selected libraries

24

- dict: Per-API or per-library limits {api/prefix: limit}

25

- 'sequential_blas_under_openmp': Special case for nested parallelism

26

- None: No limits applied (no-op)

27

28

user_api: str | None

29

API type to limit when limits is int:

30

- 'blas': Limit only BLAS libraries

31

- 'openmp': Limit only OpenMP libraries

32

- None: Limit all detected libraries

33

"""

34

35

def __init__(self, limits=None, user_api=None): ...

36

37

def __enter__(self):

38

"""Enter context manager, returns self."""

39

40

def __exit__(self, type, value, traceback):

41

"""Exit context manager, restores original limits."""

42

43

def restore_original_limits(self):

44

"""Manually restore original thread limits."""

45

46

def unregister(self):

47

"""Alias for restore_original_limits() (backward compatibility)."""

48

49

def get_original_num_threads(self):

50

"""

51

Get original thread counts before limiting.

52

53

Returns:

54

dict[str, int]: Original thread counts by user_api

55

"""

56

57

@classmethod

58

def wrap(cls, limits=None, user_api=None):

59

"""

60

Create decorator version that delays limit setting.

61

62

Returns:

63

Decorator function for use with @threadpool_limits.wrap(...)

64

"""

65

```

66

67

### Basic Usage Examples

68

69

```python

70

from threadpoolctl import threadpool_limits

71

import numpy as np

72

73

# Global thread limiting

74

with threadpool_limits(limits=1):

75

# All thread pools limited to 1 thread

76

result = np.dot(large_matrix_a, large_matrix_b)

77

78

# API-specific limiting

79

with threadpool_limits(limits=2, user_api='blas'):

80

# Only BLAS libraries limited to 2 threads

81

# OpenMP libraries keep their original limits

82

result = np.linalg.solve(A, b)

83

84

# Per-library limiting using dict

85

with threadpool_limits(limits={'libmkl_rt': 1, 'libgomp': 4}):

86

# MKL limited to 1 thread, GNU OpenMP to 4 threads

87

result = compute_intensive_operation()

88

89

# Special case for nested parallelism

90

with threadpool_limits(limits='sequential_blas_under_openmp'):

91

# Automatically handle BLAS/OpenMP interaction

92

with parallel_backend('threading', n_jobs=4): # scikit-learn example

93

result = some_parallel_computation()

94

```

95

96

### Decorator Usage

97

98

```python

99

from threadpoolctl import threadpool_limits

100

101

@threadpool_limits(limits=1)

102

def single_threaded_computation():

103

"""This function always runs with 1 thread."""

104

return np.linalg.eigvals(large_matrix)

105

106

@threadpool_limits(limits=2, user_api='blas')

107

def blas_limited_computation():

108

"""BLAS operations limited to 2 threads."""

109

return np.dot(A, B) + np.linalg.inv(C)

110

111

# Using the wrap class method

112

@threadpool_limits.wrap(limits={'openblas': 1})

113

def openblas_sequential():

114

"""OpenBLAS operations run sequentially."""

115

return np.fft.fft2(image_data)

116

```

117

118

### Advanced Limiting Patterns

119

120

```python

121

from threadpoolctl import threadpool_limits, threadpool_info

122

123

# Conditional limiting based on current state

124

current_info = threadpool_info()

125

if any(lib['num_threads'] > 8 for lib in current_info):

126

limits = 4 # Reduce high thread counts

127

else:

128

limits = None # No limiting needed

129

130

with threadpool_limits(limits=limits):

131

result = expensive_computation()

132

133

# Nested limiting (inner limits override outer)

134

with threadpool_limits(limits=4): # Outer limit

135

result1 = computation1()

136

137

with threadpool_limits(limits=1): # Inner limit overrides

138

result2 = computation2() # Runs with 1 thread

139

140

result3 = computation3() # Back to 4 threads

141

142

# Manual control with restore

143

limiter = threadpool_limits(limits=1)

144

try:

145

result = computation()

146

finally:

147

limiter.restore_original_limits()

148

```

149

150

### Error Handling

151

152

```python

153

from threadpoolctl import threadpool_limits

154

155

# Context manager handles errors gracefully

156

try:

157

with threadpool_limits(limits=1):

158

result = potentially_failing_computation()

159

except ComputationError:

160

# Thread limits are still restored even if computation fails

161

pass

162

163

# Check original limits before/after

164

limiter = threadpool_limits(limits=2)

165

original = limiter.get_original_num_threads()

166

print(f"Original BLAS threads: {original.get('blas', 'N/A')}")

167

print(f"Original OpenMP threads: {original.get('openmp', 'N/A')}")

168

```

169

170

### Special Cases

171

172

#### Sequential BLAS under OpenMP

173

174

For nested parallelism scenarios where outer parallelism uses OpenMP and inner operations use BLAS:

175

176

```python

177

from threadpoolctl import threadpool_limits

178

179

# Automatically handles BLAS/OpenMP interaction

180

with threadpool_limits(limits='sequential_blas_under_openmp'):

181

# Special logic:

182

# - If OpenBLAS with OpenMP threading: no limits applied

183

# - Otherwise: BLAS libraries limited to 1 thread

184

parallel_workload_with_blas_operations()

185

```

186

187

#### Library-Specific Limiting

188

189

```python

190

from threadpoolctl import threadpool_limits, threadpool_info

191

192

# Get specific library prefixes

193

info = threadpool_info()

194

mkl_prefix = next((lib['prefix'] for lib in info if lib['internal_api'] == 'mkl'), None)

195

196

if mkl_prefix:

197

with threadpool_limits(limits={mkl_prefix: 1}):

198

# Only MKL limited, other libraries unchanged

199

result = computation()

200

```