-
Notifications
You must be signed in to change notification settings - Fork 155
Add native SIMD testing and benchmark module #708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
190ef5c
b5ceef2
56f9afb
63e921d
143ee41
4578555
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| /* | ||
| * Copyright DataStax, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| // Google Benchmark micro-benchmarks for the fp32 vector similarity kernels: | ||
| // cosine_f32, dot_product_f32, euclidean_f32 | ||
| // | ||
| // Parameterised over the realistic embedding dimensions used in production: | ||
| // 128, 256, 512, 1024, 1536, 3072 | ||
| // | ||
| // Build (requires google-benchmark installed or available via pkg-config): | ||
| // meson setup build && ninja -C build bench_simd_kernels | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't work for me. I cloned the fork but this command fails with the following
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh it's |
||
| // | ||
| // Run: | ||
| // ./build/bench_simd_kernels [--benchmark_filter=<pattern>] | ||
|
|
||
| #include <benchmark/benchmark.h> | ||
| #include <cmath> | ||
| #include <vector> | ||
|
|
||
| #include "jvector_simd.h" | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Helpers | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| // Deterministic, non-zero float vector: avoids degenerate cosine=NaN cases. | ||
| static std::vector<float> make_vec(size_t n, float seed) | ||
| { | ||
| std::vector<float> v(n); | ||
| for (size_t i = 0; i < n; ++i) { | ||
| v[i] = seed * (1.0f + static_cast<float>(i % 7) * 0.13f); | ||
| if (i % 3 == 0) v[i] = -v[i]; | ||
| v[i] += 0.5f; | ||
| } | ||
| return v; | ||
| } | ||
|
|
||
| // Benchmark sizes matching production embedding dimensions. | ||
| static const std::vector<int64_t> kBenchSizes = {128, 256, 512, 1024, 1536, 3072}; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // dot_product_f32 | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| static void BM_dot_product_f32(benchmark::State& state) | ||
| { | ||
| const size_t n = static_cast<size_t>(state.range(0)); | ||
| auto a = make_vec(n, 0.7f); | ||
| auto b = make_vec(n, 1.3f); | ||
|
|
||
| for (auto _ : state) { | ||
| float result = dot_product_f32(a.data(), 0, b.data(), 0, n); | ||
| benchmark::DoNotOptimize(result); | ||
| } | ||
|
|
||
| state.SetItemsProcessed(state.iterations() * static_cast<int64_t>(n)); | ||
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(n) * 2 * sizeof(float)); | ||
| } | ||
| BENCHMARK(BM_dot_product_f32)->ArgsProduct({kBenchSizes}); | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // euclidean_f32 | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| static void BM_euclidean_f32(benchmark::State& state) | ||
| { | ||
| const size_t n = static_cast<size_t>(state.range(0)); | ||
| auto a = make_vec(n, 0.7f); | ||
| auto b = make_vec(n, 1.3f); | ||
|
|
||
| for (auto _ : state) { | ||
| float result = euclidean_f32(a.data(), 0, b.data(), 0, n); | ||
| benchmark::DoNotOptimize(result); | ||
| } | ||
|
|
||
| state.SetItemsProcessed(state.iterations() * static_cast<int64_t>(n)); | ||
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(n) * 2 * sizeof(float)); | ||
| } | ||
| BENCHMARK(BM_euclidean_f32)->ArgsProduct({kBenchSizes}); | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // cosine_f32 | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| static void BM_cosine_f32(benchmark::State& state) | ||
| { | ||
| const size_t n = static_cast<size_t>(state.range(0)); | ||
| auto a = make_vec(n, 0.7f); | ||
| auto b = make_vec(n, 1.3f); | ||
|
|
||
| for (auto _ : state) { | ||
| float result = cosine_f32(a.data(), 0, b.data(), 0, n); | ||
| benchmark::DoNotOptimize(result); | ||
| } | ||
|
|
||
| state.SetItemsProcessed(state.iterations() * static_cast<int64_t>(n)); | ||
| state.SetBytesProcessed(state.iterations() * static_cast<int64_t>(n) * 2 * sizeof(float)); | ||
| } | ||
| BENCHMARK(BM_cosine_f32)->ArgsProduct({kBenchSizes}); | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Entry point — benchmark::Initialize parses --benchmark_* flags. | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| BENCHMARK_MAIN(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,8 @@ | |
|
|
||
| # fail on error | ||
| set -e | ||
| # print commands as they are executed | ||
| set +x | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you want |
||
|
|
||
| # Copyright DataStax, Inc. | ||
| # | ||
|
|
@@ -17,6 +19,22 @@ set -e | |
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Path anchors — all derived from the git repository root so the script works | ||
| # regardless of the working directory it is invoked from (Maven sets | ||
| # workingDirectory to the src directory, but developers may run it from | ||
| # anywhere inside the repo). | ||
| # --------------------------------------------------------------------------- | ||
| REPO_ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)" | ||
| SCRIPT_DIR="${REPO_ROOT}/jvector-native/src/main/native/src" | ||
| NATIVE_DIR="${REPO_ROOT}/jvector-native/src/main/native" | ||
| MODULE_ROOT="${REPO_ROOT}/jvector-native" | ||
|
|
||
| HIGHWAY_DIR="${NATIVE_DIR}/third_party/highway" | ||
| BUILD_DIR="${MODULE_ROOT}/target/meson-build" | ||
| RESOURCES_DIR="${MODULE_ROOT}/src/main/resources" | ||
| JAVA_OUT_DIR="${MODULE_ROOT}/src/main/java" | ||
|
|
||
| if [ "$1" == "--auto-install-deps" ] ; then AUTO_INSTALL_DEPS=true ; shift ; fi | ||
| printf "AUTO_INSTALL_DEPS=%s\n" "${AUTO_INSTALL_DEPS}" | ||
|
|
||
|
|
@@ -29,13 +47,13 @@ if [ "$BUILDTYPE" != "release" ] && [ "$BUILDTYPE" != "debug" ] && [ "$BUILDTYPE | |
| fi | ||
| printf "BUILDTYPE=%s\n" "${BUILDTYPE}" | ||
|
|
||
| mkdir -p ../resources | ||
| mkdir -p "${RESOURCES_DIR}" | ||
|
|
||
| # compile jvector_simd_check.cpp as x86-64 | ||
| # compile jvector_simd.cpp as skylake-avx512 | ||
| # produce one shared library | ||
|
|
||
| # Check that the Google Highway submodule has been initialised | ||
| HIGHWAY_DIR="third_party/highway" | ||
| if [ ! -f "${HIGHWAY_DIR}/hwy/highway.h" ]; then | ||
| echo "ERROR: Google Highway submodule not found at ${HIGHWAY_DIR}." | ||
| echo " Run the following command from the repository root to fix this:" | ||
|
|
@@ -80,24 +98,23 @@ if [ "$(printf '%s\n' "$MIN_GCC_VERSION" "$CURRENT_GPP_VERSION" | sort -V | head | |
| exit 1 | ||
| fi | ||
|
|
||
| BUILD_DIR="../../../target/meson-build" | ||
| rm -rf ../resources/libjvector.so | ||
| rm -rf "${RESOURCES_DIR}/libjvector.so" | ||
|
|
||
| # Configure (--wipe resets any stale configuration) then compile | ||
| meson setup "${BUILD_DIR}" \ | ||
| meson setup "${BUILD_DIR}" "${NATIVE_DIR}" \ | ||
| --wipe \ | ||
| --buildtype="${BUILDTYPE}" | ||
|
|
||
| meson compile -C "${BUILD_DIR}" | ||
|
|
||
| # The versioned .so (e.g. libjvector.so.0.1.0) is the real file; symlinks point to it. | ||
| # Copy it to ../resources/ as the plain libjvector.so for Java System.load(). | ||
| # Copy it to src/main/resources/ so Maven packages it into the jar for LibraryLoader. | ||
| SOFILE=$(find "${BUILD_DIR}" -maxdepth 1 -name 'libjvector.so.*' -type f | head -1) | ||
| if [ -z "${SOFILE}" ]; then | ||
| echo "ERROR: libjvector.so not found in ${BUILD_DIR} after build." | ||
| exit 1 | ||
| fi | ||
| cp "${SOFILE}" ../resources/libjvector.so | ||
| cp "${SOFILE}" "${RESOURCES_DIR}/libjvector.so" | ||
|
|
||
| # Generate Java source code | ||
| # Should only be run when c header changes | ||
|
|
@@ -109,11 +126,12 @@ then | |
| fi | ||
|
|
||
| jextract \ | ||
| --output ../java \ | ||
| --output "${JAVA_OUT_DIR}" \ | ||
| -t io.github.jbellis.jvector.vector.cnative \ | ||
| -I . \ | ||
| -I "${SCRIPT_DIR}" \ | ||
| --header-class-name NativeSimdOps \ | ||
| jvector_simd.h | ||
| "${SCRIPT_DIR}/jvector_simd.h" | ||
|
|
||
| # Set critical linker option with heap-based segments for all generated methods | ||
| sed -i 's/DESC)/DESC, Linker.Option.critical(true))/g' ../java/io/github/jbellis/jvector/vector/cnative/NativeSimdOps.java | ||
| sed -i 's/DESC)/DESC, Linker.Option.critical(true))/g' \ | ||
| "${JAVA_OUT_DIR}/io/github/jbellis/jvector/vector/cnative/NativeSimdOps.java" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks like it is probably fine, but for future reference it would be easier to verify if this was a branch and not coming from a fork. Since it's a fork I can't just run the gha and verify the results, which would make it easier to be comfortable with approval. Not a showstopper but something to keep in mind for future PRs.