Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ jobs:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- run: sudo apt-get update && sudo apt-get install -y clang-format
- uses: astral-sh/setup-uv@v5
- run: uv sync --all-extras
- run: uv run pre-commit run --all-files
Expand All @@ -48,6 +47,9 @@ jobs:
- run: uv run python examples/02_plotting.py
- run: uv run python examples/03_allocators.py
- run: uv run python examples/04_sources.py
- run: >
uv run jupyter nbconvert --execute --to notebook
--output-dir executed_notebooks notebooks/*.ipynb

test:
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -79,25 +81,14 @@ jobs:
# Gate coverage only where every optional path runs: Hub rate limits and
# the Linux-only minimalloc install starve the other legs of statements
- run: >
uv run pytest -v
uv run pytest -v -n auto --dist worksteal
${{ matrix.os == 'ubuntu-latest'
&& matrix.python-version == env.DEFAULT_PYTHON
&& '--cov=omnimalloc --cov-fail-under=90 --cov-report=term-missing'
|| '' }}

build:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: astral-sh/setup-uv@v5
- run: uv sync --all-extras
- run: uv build

# TODO(fpedd): clang-tidy and iwyu findings still only get reported, not
# enforced; compiler warnings do fail the job via -Werror.
# Also builds the sdist and wheel. TODO(fpedd): clang-tidy and iwyu findings
# still only get reported, not enforced; compiler warnings fail via -Werror.
cpp-quality:
runs-on: ubuntu-latest
timeout-minutes: 60
Expand All @@ -115,7 +106,7 @@ jobs:
checks-status:
runs-on: ubuntu-latest
if: always()
needs: [pre-commit, examples, test, build, cpp-quality]
needs: [pre-commit, examples, test, cpp-quality]
steps:
- run: exit 1
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
14 changes: 9 additions & 5 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ jobs:
fetch-depth: 0
- uses: astral-sh/setup-uv@v5
- run: uv sync --all-extras
- run: uv pip install ${{ env.MINIMALLOC_URL }}
# Fuzz every workload and allocator, then the optimality, timeout and pin oracles
- run: uv run python fuzz.py --deep
working-directory: scripts/stress
- run: uv run python oracles.py
working-directory: scripts/stress
- run: uv run python scripts/stress_omni.py
# Two workers keep greedy_by_all's per-process copies inside runner memory
- run: uv run python scripts/benchmark_allocation.py --max-threads 2
Expand All @@ -51,6 +57,7 @@ jobs:
with:
name: nightly-figures
path: |
scripts/stress/fuzz_results_stress/
stress_results_omni/
benchmark_results_allocation/
benchmark_results_pressure/
Expand Down Expand Up @@ -108,8 +115,8 @@ jobs:
- run: uv run ruff format --check
- run: uv run ty check

# The examples job in checks.yml stops at 04; notebooks run nowhere else.
examples-notebooks:
# The examples job in checks.yml stops at 04: 05 is slow and hits the HF Hub.
examples-benchmark:
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
Expand All @@ -120,6 +127,3 @@ jobs:
- run: uv sync --all-extras
- run: uv pip install ${{ env.MINIMALLOC_URL }}
- run: uv run python examples/05_benchmark.py
- run: >
uv run jupyter nbconvert --execute --to notebook
--output-dir executed_notebooks notebooks/*.ipynb
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ repos:
- id: check-merge-conflict
- id: name-tests-test
args: [--pytest-test-first]
exclude: '^tests/(markers|paths)\.py$'
exclude: '^tests/markers\.py$'

- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.8
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ Run your own campaigns with the [benchmark harness](examples/05_benchmark.py).
# Initial setup
git clone git@github.com:fpedd/omnimalloc.git
cd omnimalloc
uv sync --all-extras --group dev
uv sync --all-extras

# Run tests, linting, type checking
uv run pytest
uv run pytest -n auto
uv run ruff check --fix && uv run ruff format && uv run ty check

# Setup pre-commit hooks (run once)
Expand Down
19 changes: 1 addition & 18 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ all = [
"deap>=1.4.0",
"huggingface-hub>=0.35.0",
"matplotlib>=3.10.0",
"numpy>=1.25.0",
"onnx>=1.19.0",
"tqdm>=4.66.0",
]
Expand Down Expand Up @@ -86,7 +85,7 @@ test-command = "pytest {project}/tests/unit -x --no-header -q"

[tool.cibuildwheel.linux]
archs = ["auto"]
before-all = "yum install -y cmake || apk add cmake"
before-all = "yum install -y cmake"

[tool.cibuildwheel.macos]
archs = ["arm64"]
Expand All @@ -102,8 +101,6 @@ exclude = ["*.ipynb", "*.pyi"]
[tool.ruff.lint]
select = ["ALL"]
ignore = [
"B019", # cache decorator
"B027", # non-abstract methods in abc
"COM812", # trailing comma (formatter)
"CPY001", # copyright notice
"D100", # module docstring
Expand All @@ -113,36 +110,24 @@ ignore = [
"D104", # package docstring
"D105", # magic method docstring
"D107", # init docstring
"D202", # blank lines in docstring
"D203", # incompatible with D211
"D213", # incompatible with D212
"D401", # imperative mood
"D413", # blank line after last section
"DTZ005", # datetime.now()
"EM101", # string literals in exceptions
"EM102", # f-strings in exceptions
"FBT001", # boolean positional args
"FBT002", # boolean default args
"FIX002", # TODOs
"G004", # f-strings in logging
"INP001", # allow scripts without init
"N801", # class names not CamelCase
"PERF203", # try-except inside loop
"PGH003", # generic type: ignore
"PIE790", # ellipsis in functions
"PLC0415", # import outside top-level
"PLR0913", # too many arguments
"PLR0917", # too many positional arguments
"PLR2004", # magic value comparison
"PLW2901", # reassign for loop vars
"PYI019", # custom TypeVar
"PYI034", # return Self in dunders
"RET504", # unnecessary variable before return
"S101", # assert
"S311", # pseudo-random generators
"S603", # allow untrusted subprocess
"SIM102", # nested if
"SIM110", # for loop replaceable with any()
"T201", # print()
"TD003", # missing issue link
"TRY003", # long exception messages
Expand All @@ -158,8 +143,6 @@ log_cli_level = "INFO"
log_cli_format = "%(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)"
log_cli_date_format = "%Y-%m-%d %H:%M:%S"

[tool.ty]

[tool.ty.rules]
# Ignores guarding optional imports are needed without the dep, unused with it
unused-type-ignore-comment = "ignore"
Expand Down
124 changes: 124 additions & 0 deletions scripts/_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#
# SPDX-License-Identifier: Apache-2.0
#
"""House style and helpers shared by the benchmark and stress scripts."""

from math import isnan, nan
from statistics import mean
from typing import TYPE_CHECKING, Any

import matplotlib.pyplot as plt

if TYPE_CHECKING:
from matplotlib.axes import Axes
from matplotlib.figure import Figure

SURFACE = "#fcfcfb"
INK = "#0b0b0b"
INK_SECONDARY = "#52514e"
INK_MUTED = "#898781"
GRID = "#e1e0d9"
AXIS = "#c3c2b7"
# Most-separated hues first; per-series markers keep identity readable
SERIES = ("#2a78d6", "#1baf7a", "#eda100", "#e34948", "#4a3aa7", "#008300", "#e87ba4")
MARKERS = ("o", "s", "^", "D", "v", "P", "X")

Sample = dict[str, Any]


def fmt_seconds(seconds: float) -> str:
if isnan(seconds):
return "-"
if seconds < 1e-3:
return f"{seconds * 1e6:.0f} us"
if seconds < 1.0:
return f"{seconds * 1e3:.1f} ms"
return f"{seconds:.2f} s"


def series_means(
samples: list[Sample],
sizes: list[int],
family: str,
name: str,
key: str,
expected: int,
) -> list[float]:
"""Mean of `key[name]` per size; NaN where fewer than `expected` samples."""
means = []
for size in sizes:
values = [
s[key][name]
for s in samples
if s["family"] == family and s["size"] == size and name in s.get(key, {})
]
means.append(mean(values) if len(values) >= expected else nan)
return means


def two_panel_figure() -> "tuple[Figure, Axes, Axes]":
"""A tall time panel over a short quality panel sharing the x axis."""
fig, (top, bottom) = plt.subplots(
2,
1,
sharex=True,
figsize=(8, 6.4),
height_ratios=(3, 1.4),
layout="constrained",
)
fig.set_facecolor(SURFACE)
return fig, top, bottom


def style_axes(ax: "Axes") -> None:
ax.set_facecolor(SURFACE)
ax.grid(visible=True, which="major", color=GRID, linewidth=0.8)
ax.set_axisbelow(True)
ax.tick_params(colors=INK_MUTED, labelcolor=INK_SECONDARY, labelsize=9)
for side in ("top", "right"):
ax.spines[side].set_visible(False)
for side in ("bottom", "left"):
ax.spines[side].set_color(AXIS)


def log_time_axes(ax: "Axes", xs: list[int], base: int = 10) -> None:
style_axes(ax)
ax.set_yscale("log")
ax.set_xscale("log", base=base)
ax.set_xticks(xs, [f"{x:,}" for x in xs])
ax.tick_params(which="minor", bottom=False)
ax.set_ylabel("wall time [s]", color=INK_SECONDARY, fontsize=10)


def plot_series(
ax: "Axes",
xs: list[int],
values: list[float],
label: str,
color: str,
marker: str = "o",
linestyle: str = "-",
) -> None:
# An all-NaN series leaves a log axis empty and crashes on save
if all(isnan(value) for value in values):
return
ax.plot(
xs,
values,
label=label,
color=color,
linestyle=linestyle,
linewidth=2,
marker=marker,
markersize=5.5,
markeredgecolor=SURFACE,
markeredgewidth=1,
)


def titles(ax: "Axes", title: str, caption: str) -> None:
ax.set_title(title, loc="left", color=INK, fontsize=12, fontweight="medium", pad=22)
note = ax.text(
0.0, 1.04, caption, transform=ax.transAxes, fontsize=8.5, color=INK_MUTED
)
note.set_in_layout(False)
Loading
Loading