Skip to content

Commit 32b3ece

Browse files
authored
Add option to use mimalloc as global allocator (#16024)
* Add option to use mimalloc as global allocator This commit adds a new feature to the cext and pyext crates to switch the global allocator to use mimalloc [1] instead of the system allocator. mimalloc promises better allocation performance and also lower resident set size over glibc, macOS's libmalloc, or window's allocator [2]. In local benchmarking using mimalloc improves runtime performance on transpiler bencmarks in asv about 10% on my x86_64 benchmarking system. The tradeoff for this is the mimalloc crate [3] internally builds and statically links the mimalloc c library from source as part of cargo's build process. This is all encapsulated in the mimalloc-sys's build.rs using the cc [4] crate so it embeds very cleanly into the build system. However, this does however require that a C compiler is available when building from source. For building the Python package and ironically the C standalone lib this was previously not a requirement. When the feature is enabled it switches the global allocator to use mimalloc. Since this is global for a compilation artifact we can only do it once in qiskit crate, and we'll want to do it either in pyext xor cext based on whether we're building a python extension or c standalone lib so that only the outermost crate is opinionated about which allocator to use. By default this feature is disabled on both crates and we opt-in to it in the build system. For building pyext the setup.py file updates the features we pass to setuptools-rust when building the extension to enable the mimalloc crate and set the global allocator in the pyext code. Similarly, the makefile is updated to do the same for build cext in standalone mode. If we don't want to make this enabled by default (primarily because of the dependency on a C compiler) for building from source we can update the logic in the build scripts to expose an option and only enable in CI for testing and for release artifacts. This is something we'll need to discuss before releasing this option. [1] https://github.com/microsoft/mimalloc [2] Leijen, D., Zorn, B., de Moura, L. (2019). Mimalloc: Free List Sharding in Action. In: Lin, A. (eds) Programming Languages and Systems. APLAS 2019. Lecture Notes in Computer Science(), vol 11893. Springer, Cham. https://doi.org/10.1007/978-3-030-34175-6_13 [3] https://crates.io/crates/mimalloc [4] https://docs.rs/cc/latest/cc/ * Make mimalloc opt-in by default This commit switches the mimalloc feature to be opt-in by default everywhere. Since building mimalloc requires a C compiler and this does significantly change the requirements for building Qiskit from source, making it opt-in for people that want it is the best course of action for now. This commit adds a new environment variable `QISKIT_BUILD_WITH_MIMALLOC=1` which is used to enable building mimalloc when building Qiskit. The CI jobs are updated to mix mimalloc and not in Python test jobs and also to always enable mimalloc in wheel builds for release. * Improve CONTRIBUTING.md build section formatting * Improve release notes * Enable mimalloc for asv This commti adds the mimalloc environment variable to the asv config too so that we are enabling the use of mimalloc when running asv numbers. This will mean the performance numbers we measure with asv are showing the performance with mimalloc. This is desired as it will better capture the performance of the packages we ship on release.
1 parent 43e9c17 commit 32b3ece

17 files changed

Lines changed: 113 additions & 22 deletions

.github/workflows/branch-protection.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ jobs:
106106
install-optionals: true
107107
install-from-sdist: false
108108
qiskit-pycache: false
109+
mimalloc: false
109110
- id: "Python newest"
110111
python: ${{ needs.config.outputs.python-new }}
111112
install-optionals: false
@@ -127,6 +128,7 @@ jobs:
127128
- id: "Python oldest"
128129
python: ${{ needs.config.outputs.python-old }}
129130
install-optionals: true
131+
mimalloc: false
130132
- id: "Python newest"
131133
python: ${{ needs.config.outputs.python-new }}
132134
install-optionals: false
@@ -146,6 +148,7 @@ jobs:
146148
- id: "Python oldest"
147149
python: ${{ needs.config.outputs.python-old }}
148150
install-optionals: true
151+
mimalloc: false
149152
- id: "Python newest"
150153
python: ${{ needs.config.outputs.python-new }}
151154
install-optionals: false

.github/workflows/test-linux.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ on:
2929
structures when building Qiskit.
3030
type: boolean
3131
default: true
32+
mimalloc:
33+
description: >
34+
Whether to build with mimalloc or not
35+
type: boolean
36+
default: true
3237

3338
jobs:
3439
unit-tests:
@@ -61,6 +66,9 @@ jobs:
6166
- name: Set gate-caching settings for build
6267
if: ${{ !inputs.qiskit-pycache }}
6368
run: 'echo "QISKIT_NO_CACHE_GATES=1" >> $GITHUB_ENV'
69+
- name: Set mimalloc for build
70+
if: ${{ inputs.mimalloc }}
71+
run: 'echo "QISKIT_BUILD_WITH_MIMALLOC=1" >> $GITHUB_ENV'
6472
- name: Install Qiskit from sdist
6573
if: ${{ inputs.install-from-sdist }}
6674
run: |

.github/workflows/test-mac.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@ on:
88
Python version to currently test
99
type: string
1010
required: true
11-
1211
install-optionals:
1312
description: >
1413
Decides whether we install optyional dependencies
1514
type: boolean
1615
default: false
17-
1816
runner:
1917
description: >
2018
Describes the system this workflow should run on.
2119
type: string
2220
required: true
21+
mimalloc:
22+
description: >
23+
Whether to build with mimalloc or not
24+
type: boolean
25+
default: true
26+
2327
jobs:
2428
tests-mac:
2529
name: ${{ inputs.runner }} - Python ${{ inputs.python-version }}
@@ -42,6 +46,9 @@ jobs:
4246
stestr | "${{ runner.os }}"
4347
stestr
4448
path: .stestr
49+
- name: Set mimalloc for build
50+
if: ${{ inputs.mimalloc }}
51+
run: 'echo "QISKIT_BUILD_WITH_MIMALLOC=1" >> $GITHUB_ENV'
4552
- name: "Install dependencies"
4653
run: |
4754
set -e

.github/workflows/test-windows.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@ on:
88
Python version to currently test
99
type: string
1010
required: true
11-
1211
runner:
1312
description: >
1413
Describes the system this workflow should run on.
1514
type: string
1615
required: true
17-
1816
install-optionals:
1917
description: >
2018
Decides whether we install optyional dependencies
2119
type: boolean
2220
default: false
21+
mimalloc:
22+
description: >
23+
Whether to build with mimalloc or not
24+
type: boolean
25+
default: true
2326
jobs:
2427
test-windows:
2528
name: ${{ inputs.runner }} - Python ${{ inputs.python-version }}
@@ -42,6 +45,9 @@ jobs:
4245
stestr | "${{ runner.os }}"
4346
stestr
4447
path: .stestr
48+
- name: Set mimalloc for build
49+
if: ${{ inputs.mimalloc }}
50+
run: 'echo "QISKIT_BUILD_WITH_MIMALLOC=1" >> $GITHUB_ENV'
4551
- name: Install dependencies
4652
run: |
4753
python -m venv test-job

.github/workflows/wheels-build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ jobs:
108108
cat >>"$GITHUB_ENV" <<EOF
109109
CIBW_BEFORE_ALL_LINUX=dnf install -y wget && {package}/tools/install_rust.sh
110110
CIBW_BEFORE_BUILD=bash ./tools/build_pgo.sh $PGO_WORK_DIR $PGO_OUT_PATH
111-
CIBW_ENVIRONMENT=RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function'
112-
CIBW_ENVIRONMENT_MACOS=MACOSX_DEPLOYMENT_TARGET='10.12' RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function'
113-
CIBW_ENVIRONMENT_LINUX=RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function' PATH="\$PATH:\$HOME/.cargo/bin" CARGO_NET_GIT_FETCH_WITH_CLI="true"
111+
CIBW_ENVIRONMENT=RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function' QISKIT_BUILD_WITH_MIMALLOC=1
112+
CIBW_ENVIRONMENT_MACOS=MACOSX_DEPLOYMENT_TARGET='10.12' RUSTUP_TOOLCHAIN=stable RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function' QISKIT_BUILD_WITH_MIMALLOC=1
113+
CIBW_ENVIRONMENT_LINUX=RUSTUP_TOOLCHAIN=stable QISKIT_BUILD_WITH_MIMALLOC=1 RUSTFLAGS='-Cprofile-use=$PGO_OUT_PATH -Cllvm-args=-pgo-warn-missing-function' PATH="\$PATH:\$HOME/.cargo/bin" CARGO_NET_GIT_FETCH_WITH_CLI="true"
114114
EOF
115115
env:
116116
PGO_WORK_DIR: ${{ github.workspace }}/pgo-data

CONTRIBUTING.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,27 @@ to `build_rust` overrides this default.
165165

166166
### Compile time options
167167

168-
When building qiskit from source there are options available to control how
169-
Qiskit is built. Right now the only option is if you set the environment
170-
variable `QISKIT_NO_CACHE_GATES=1` this will disable runtime caching of
171-
Python gate objects when accessing them from a `QuantumCircuit` or `DAGCircuit`.
172-
This makes a tradeoff between runtime performance for Python access and memory
173-
overhead. Caching gates will result in better runtime for users of Python at
174-
the cost of increased memory consumption. If you're working with any custom
175-
transpiler passes written in Python or are otherwise using a workflow that
176-
repeatedly accesses the `operation` attribute of a `CircuitInstruction` or `op`
177-
attribute of `DAGOpNode` enabling caching is recommended.
168+
When building Qiskit from source there are options available to control how
169+
Qiskit is built. These options are set with the following environment variables:
170+
171+
* `QISKIT_BUILD_WITH_MIMALLOC=1`: this will enable using
172+
[mimalloc](https://github.com/microsoft/mimalloc) as the global allocator for
173+
Qiskit instead of the default system allocator. This improves the runtime and
174+
memory performance of Qiskit but will require having a C compiler installed
175+
when building Qiskit.
176+
* `QISKIT_NO_CACHE_GATES=1`: this will disable runtime caching of
177+
Python gate objects when accessing them from a `QuantumCircuit` or `DAGCircuit`.
178+
This makes a tradeoff between runtime performance for Python access and memory
179+
overhead. Caching gates will result in better runtime for users of Python at
180+
the cost of increased memory consumption. If you're working with any custom
181+
transpiler passes written in Python or are otherwise using a workflow that
182+
repeatedly accesses the `operation` attribute of a `CircuitInstruction` or `op`
183+
attribute of `DAGOpNode` enabling caching is recommended.
184+
185+
These environment variables are only valid when building Qiskit the Python package
186+
with a PEP 517 compatible build tool or calling `setup.py` directly.
187+
Or as a standalone C library with `make c` (`QISKIT_NO_CACHE_GATES` has no effect
188+
when building a standalone C library).
178189

179190
## Issues and pull requests
180191

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ uuid = { version = "1.23", features = ["v4", "fast-rng"], default-features = fal
4343
anyhow = "1.0"
4444
binrw = "0.15"
4545
cbindgen = "0.29.2"
46+
mimalloc = "0.1.48"
4647

4748
# Most of the crates don't need the feature `extension-module`, since only `qiskit-pyext` builds an
4849
# actual C extension (the feature disables linking in `libpython`, which is forbidden in Python

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ ifneq ($(OS), Windows_NT)
1414
OS := $(shell uname -s)
1515
endif
1616

17+
ifeq ($(QISKIT_BUILD_WITH_MIMALLOC), 1)
18+
MIMALLOC := --features=mimalloc
19+
endif
20+
1721
.PHONY: default ruff env lint lint-incr style black test test_randomized pytest pytest_randomized test_ci coverage coverage_erase clean
1822

1923
default: style lint-incr test ;
@@ -135,7 +139,7 @@ fix_cformat:
135139
# instead.
136140
.PHONY: build-clib build-clib-release build-clib-dev
137141
build-clib:
138-
cargo rustc -p qiskit-cext --crate-type cdylib ${C_LIB_CARGO_FLAGS} -- ${C_LIB_RUSTC_FLAGS}
142+
cargo rustc -p qiskit-cext ${MIMALLOC} --crate-type cdylib ${C_LIB_CARGO_FLAGS} -- ${C_LIB_RUSTC_FLAGS}
139143
build-clib-release: C_LIB_CARGO_FLAGS=--release
140144
build-clib-release: build-clib
141145
build-clib-dev: C_LIB_CARGO_FLAGS=--profile dev

asv.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
],
1212
"build_command": [
1313
"python -m pip install -U build",
14-
"python -m build --outdir {build_cache_dir} --wheel {build_dir}"
14+
"QISKIT_BUILD_WITH_MIMALLOC=1 python -m build --outdir {build_cache_dir} --wheel {build_dir}"
1515
],
1616
"branches": ["main"],
1717
"dvcs": "git",

0 commit comments

Comments
 (0)