mirror of
https://github.com/lancedb/lancedb.git
synced 2026-08-18 12:08:35 +00:00
7b6ee0d655
Tracks #3324. On x86_64 CPUs without AVX2 (Sandy Bridge / Ivy Bridge / Westmere on Intel; Bulldozer / Piledriver / Steamroller on AMD), `import lancedb` SIGILLs because the wheel bakes AVX2 + FMA into every compiled function. Per [westonpace's review](https://github.com/lancedb/lancedb/issues/3324#issuecomment-4328944354), the default `lancedb` wheel stays fast; pre-Haswell users get a separately-published `lancedb-compat` wheel. ## Summary - Adds a `lancedb-compat` matrix entry to `pypi-publish.yml` that builds with `RUSTFLAGS="-C target-cpu=x86-64-v2"` (Nehalem-class baseline). Same Python API (`import lancedb` works) — files install to the same namespace, so the two wheels conflict at install time and users pick one. Same pattern as `psycopg2` / `psycopg2-binary` and `tensorflow` / `tensorflow-cpu`. - Generalizes `build_linux_wheel` and `upload_wheel` composites with optional `package-name` and `rustflags` inputs (defaults preserve the existing 4 `lancedb` matrix entries verbatim). - Documents the choice in `python/README.md`: `pip install lancedb-compat` for pre-Haswell hosts. The default `.cargo/config.toml` baseline is unchanged. ## Sequencing 1. ~~lance-format/lance#6630 merges → runtime SIMD dispatch lands in lance.~~ **Done — merged.** 2. lancedb's lance dep is bumped to a release that includes it (separate PR / normal cadence). 3. This PR's `lancedb-compat` wheel build path starts producing a wheel that runs on pre-Haswell hardware. **Maintainer setup**: register `lancedb-compat` on PyPI and configure trusted publishing. ## Verified end-to-end on Sandy Bridge Xeon E5-2609 Verification was done locally against a fork-pinned lance dep that includes the runtime dispatch implementation, using the same `RUSTFLAGS="-C target-cpu=x86-64-v2"` flags this PR uses in CI: ``` $ RUSTFLAGS="-C target-cpu=x86-64-v2" maturin build --release $ pip install ./target/wheels/lancedb-*.whl $ python verify.py PASS: import + simd dispatch + table create + vector search all work. ``` Pre-fix on the same CPU (default `pip install lancedb`): `Illegal instruction (core dumped)`. Full reproducer (deps + clone + build + verification): https://gist.github.com/tobocop2/2e341358b55c143527416edfdb1e37df. Fork-internal verification PR with the dep bump and full logs: [`tobocop2/lancedb#2`](https://github.com/tobocop2/lancedb/pull/2). ## Benchmarks — no regressions on modern CPUs from the lance-side change These are the numbers I ran for the lance PR, confirming the runtime dispatch doesn't slow down the default (`target-cpu=haswell`) wheel that existing users install. Criterion, one machine, one session, base → PR, no `RUSTFLAGS` override. Full methodology, null experiments, and logs: [lance-format/lance#6630 benchmark comment](https://github.com/lance-format/lance/pull/6630#issuecomment-4933063394) and the [logs gist](https://gist.github.com/tobocop2/3c6d0f449cbd736aa2501f89a7fe56a2). | benchmark | EPYC 7B13 (`avx2`, `fma`, no `avx512f`) | Xeon Cascade Lake (`avx512f`) | |---|---|---| | `Cosine(f32, scalar)` *(control)* | +0.04% | +0.09% | | `Cosine(f64, scalar)` | −0.34% | −1.94% | | `Cosine(u8, SIMD)` | +2.30% | +3.63% | | `Dot(f16, SIMD)` | −0.58% | +0.61% | | `Dot(f32, SIMD)` | +0.34% | **−6.08%** | | `Dot(f32, arrow_arity)` | +0.02% | −0.00% | | `L2(f32, scalar)` | −0.10% | −0.02% | | `L2(f32, simd)` (dim 1024) | +2.63% | −0.53% | | **`L2(simd,f32x8)` (dim 8)** | **−45.9%** | **−25.1%** | | `L2(u8, SIMD)` | +0.42% | −3.11% | | `NormL2(f32, SIMD)` | −1.02% | −4.17% | | `NormL2(f64, SIMD)` | +3.51% | −0.58% | Nothing regresses beyond the noise floor. Dim 8 — the PQ sub-vector width — improves 25–46%. --- To be transparent: this isn't my domain of expertise and the lance-side implementation is AI-generated. I verified it works end-to-end on the failing hardware. Happy to roll in feedback.
85 lines
3.5 KiB
YAML
85 lines
3.5 KiB
YAML
# We create a composite action to be re-used both for testing and for releasing
|
|
name: build-linux-wheel
|
|
description: "Build a manylinux wheel for lance"
|
|
inputs:
|
|
python-minor-version:
|
|
description: "10, 11, 12, 13"
|
|
required: true
|
|
args:
|
|
description: "--release"
|
|
required: false
|
|
default: ""
|
|
arm-build:
|
|
description: "Build for arm64 instead of x86_64"
|
|
# Note: this does *not* mean the host is arm64, since we might be cross-compiling.
|
|
required: false
|
|
default: "false"
|
|
manylinux:
|
|
description: "The manylinux version to build for"
|
|
required: false
|
|
default: "2_17"
|
|
package-name:
|
|
description: "Override [project] name in python/pyproject.toml (e.g. 'lancedb-compat'). Default keeps 'lancedb'."
|
|
required: false
|
|
default: "lancedb"
|
|
rustflags:
|
|
description: "RUSTFLAGS for the build container, as a single whitespace-free token (e.g. '-Ctarget-cpu=x86-64-v2'). Empty leaves RUSTFLAGS unset, keeping the defaults from .cargo/config.toml."
|
|
required: false
|
|
default: ""
|
|
runs:
|
|
using: "composite"
|
|
steps:
|
|
- name: CONFIRM ARM BUILD
|
|
shell: bash
|
|
env:
|
|
ARM_BUILD: ${{ inputs.arm-build }}
|
|
run: |
|
|
echo "ARM BUILD: $ARM_BUILD"
|
|
- name: Patch package name for variant build
|
|
if: ${{ inputs.package-name != 'lancedb' }}
|
|
shell: bash
|
|
env:
|
|
PACKAGE_NAME: ${{ inputs.package-name }}
|
|
run: |
|
|
# Swap the [project] name so this build produces e.g. lancedb-compat
|
|
# wheels. The package still installs files under the lancedb/
|
|
# namespace -- import lancedb still works after pip install.
|
|
sed -i.bak 's/^name = "lancedb"$/name = "'"$PACKAGE_NAME"'"/' python/pyproject.toml
|
|
rm -f python/pyproject.toml.bak
|
|
grep '^name = ' python/pyproject.toml
|
|
- name: Build x86_64 Manylinux wheel
|
|
if: ${{ inputs.arm-build == 'false' }}
|
|
uses: PyO3/maturin-action@v1
|
|
with:
|
|
maturin-version: "1.12.4"
|
|
command: build
|
|
working-directory: python
|
|
docker-options: "-e PIP_EXTRA_INDEX_URL='https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/' -e PROTOC=/usr/local/bin/protoc ${{ inputs.rustflags != '' && format('-e RUSTFLAGS={0}', inputs.rustflags) || '' }}"
|
|
target: x86_64-unknown-linux-gnu
|
|
manylinux: ${{ inputs.manylinux }}
|
|
args: ${{ inputs.args }}
|
|
before-script-linux: |
|
|
set -e
|
|
curl -fsSL https://github.com/protocolbuffers/protobuf/releases/download/v24.4/protoc-24.4-linux-x86_64.zip -o /tmp/protoc.zip
|
|
unzip /tmp/protoc.zip -d /usr/local
|
|
rm /tmp/protoc.zip
|
|
/usr/local/bin/protoc --version
|
|
- name: Build Arm Manylinux Wheel
|
|
if: ${{ inputs.arm-build == 'true' }}
|
|
uses: PyO3/maturin-action@v1
|
|
with:
|
|
maturin-version: "1.12.4"
|
|
command: build
|
|
working-directory: python
|
|
docker-options: "-e PIP_EXTRA_INDEX_URL='https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/' -e PROTOC=/usr/local/bin/protoc ${{ inputs.rustflags != '' && format('-e RUSTFLAGS={0}', inputs.rustflags) || '' }}"
|
|
target: aarch64-unknown-linux-gnu
|
|
manylinux: ${{ inputs.manylinux }}
|
|
args: ${{ inputs.args }}
|
|
before-script-linux: |
|
|
set -e
|
|
yum install -y clang
|
|
curl -fsSL https://github.com/protocolbuffers/protobuf/releases/download/v24.4/protoc-24.4-linux-aarch_64.zip -o /tmp/protoc.zip
|
|
unzip /tmp/protoc.zip -d /usr/local
|
|
rm /tmp/protoc.zip
|
|
/usr/local/bin/protoc --version
|