Spaces:
Running
Running
Download cpp_engine/simd_engine.cpp from NOT-OMEGA/SentinelAI: direct link, hf CLI and curl.
- Browser
- Download file 15.5 kB
-
https://huggingface.co/spaces/NOT-OMEGA/SentinelAI/resolve/main/cpp_engine/simd_engine.cpp
- Command line
-
hf download hf://spaces/NOT-OMEGA/SentinelAI/cpp_engine/simd_engine.cpp
-
curl -L -o simd_engine.cpp https://huggingface.co/spaces/NOT-OMEGA/SentinelAI/resolve/main/cpp_engine/simd_engine.cpp
15.5 kB
| // simd_engine.cpp — C++17 AVX2 + FMA Vector Engine | |
| // Provides: cosine_similarity, top_k_search, cache_lookup | |
| // Compiled with: -O3 -mavx2 -mfma -std=c++17 | |
| namespace py = pybind11; | |
| // ───────────────────────────────────────────────── | |
| // AVX2 + FMA Dot Product (384-dim optimized) | |
| // ───────────────────────────────────────────────── | |
| static inline float avx2_dot_product(const float* a, const float* b, int n) { | |
| __m256 sum = _mm256_setzero_ps(); | |
| int i = 0; | |
| // Process 32 floats per iteration (4 x 8-wide AVX2 registers) | |
| for (; i + 31 < n; i += 32) { | |
| __m256 va0 = _mm256_loadu_ps(a + i); | |
| __m256 vb0 = _mm256_loadu_ps(b + i); | |
| sum = _mm256_fmadd_ps(va0, vb0, sum); | |
| __m256 va1 = _mm256_loadu_ps(a + i + 8); | |
| __m256 vb1 = _mm256_loadu_ps(b + i + 8); | |
| sum = _mm256_fmadd_ps(va1, vb1, sum); | |
| __m256 va2 = _mm256_loadu_ps(a + i + 16); | |
| __m256 vb2 = _mm256_loadu_ps(b + i + 16); | |
| sum = _mm256_fmadd_ps(va2, vb2, sum); | |
| __m256 va3 = _mm256_loadu_ps(a + i + 24); | |
| __m256 vb3 = _mm256_loadu_ps(b + i + 24); | |
| sum = _mm256_fmadd_ps(va3, vb3, sum); | |
| } | |
| // Process remaining 8-wide chunks | |
| for (; i + 7 < n; i += 8) { | |
| __m256 va = _mm256_loadu_ps(a + i); | |
| __m256 vb = _mm256_loadu_ps(b + i); | |
| sum = _mm256_fmadd_ps(va, vb, sum); | |
| } | |
| // Horizontal reduction | |
| __m128 hi = _mm256_extractf128_ps(sum, 1); | |
| __m128 lo = _mm256_castps256_ps128(sum); | |
| __m128 s = _mm_add_ps(lo, hi); | |
| s = _mm_hadd_ps(s, s); | |
| s = _mm_hadd_ps(s, s); | |
| float result = _mm_cvtss_f32(s); | |
| // Scalar tail | |
| for (; i < n; i++) { | |
| result += a[i] * b[i]; | |
| } | |
| return result; | |
| // Scalar fallback | |
| float result = 0.0f; | |
| for (int i = 0; i < n; i++) { | |
| result += a[i] * b[i]; | |
| } | |
| return result; | |
| } | |
| // ───────────────────────────────────────────────── | |
| // L2 Norm (AVX2 accelerated) | |
| // ───────────────────────────────────────────────── | |
| static inline float avx2_l2_norm(const float* a, int n) { | |
| return std::sqrt(avx2_dot_product(a, a, n)); | |
| } | |
| // ───────────────────────────────────────────────── | |
| // Cosine Similarity (for L2-normalized vectors, this is just dot product) | |
| // ───────────────────────────────────────────────── | |
| float cosine_similarity(py::array_t<float> a, py::array_t<float> b) { | |
| auto buf_a = a.request(); | |
| auto buf_b = b.request(); | |
| if (buf_a.ndim != 1 || buf_b.ndim != 1) | |
| throw std::runtime_error("Input arrays must be 1-dimensional"); | |
| if (buf_a.shape[0] != buf_b.shape[0]) | |
| throw std::runtime_error("Input arrays must have the same length"); | |
| int n = static_cast<int>(buf_a.shape[0]); | |
| const float* pa = static_cast<const float*>(buf_a.ptr); | |
| const float* pb = static_cast<const float*>(buf_b.ptr); | |
| float dot = avx2_dot_product(pa, pb, n); | |
| float norm_a = avx2_l2_norm(pa, n); | |
| float norm_b = avx2_l2_norm(pb, n); | |
| if (norm_a < 1e-9f || norm_b < 1e-9f) return 0.0f; | |
| return dot / (norm_a * norm_b); | |
| } | |
| // ───────────────────────────────────────────────── | |
| // Top-K Search: returns indices and scores of top-k most similar vectors | |
| // ───────────────────────────────────────────────── | |
| struct SearchResult { | |
| std::vector<int> indices; | |
| std::vector<float> scores; | |
| }; | |
| SearchResult top_k_search( | |
| py::array_t<float> query, // shape: (dim,) | |
| py::array_t<float> matrix, // shape: (num_vectors, dim) | |
| int k, | |
| float threshold = -1.0f // optional minimum similarity threshold | |
| ) { | |
| auto q_buf = query.request(); | |
| auto m_buf = matrix.request(); | |
| if (q_buf.ndim != 1) | |
| throw std::runtime_error("Query must be 1-dimensional"); | |
| if (m_buf.ndim != 2) | |
| throw std::runtime_error("Matrix must be 2-dimensional"); | |
| if (q_buf.shape[0] != m_buf.shape[1]) | |
| throw std::runtime_error("Query dimension must match matrix column dimension"); | |
| int dim = static_cast<int>(q_buf.shape[0]); | |
| int num_vectors = static_cast<int>(m_buf.shape[0]); | |
| const float* q_ptr = static_cast<const float*>(q_buf.ptr); | |
| const float* m_ptr = static_cast<const float*>(m_buf.ptr); | |
| if (k <= 0) k = 1; | |
| if (k > num_vectors) k = num_vectors; | |
| // Compute query norm once | |
| float q_norm = avx2_l2_norm(q_ptr, dim); | |
| if (q_norm < 1e-9f) { | |
| return {{}, {}}; | |
| } | |
| // Compute all similarities | |
| std::vector<std::pair<float, int>> scores_idx; | |
| scores_idx.reserve(num_vectors); | |
| for (int i = 0; i < num_vectors; i++) { | |
| const float* vec_ptr = m_ptr + (static_cast<size_t>(i) * dim); | |
| float dot = avx2_dot_product(q_ptr, vec_ptr, dim); | |
| float v_norm = avx2_l2_norm(vec_ptr, dim); | |
| float sim = (v_norm < 1e-9f) ? 0.0f : dot / (q_norm * v_norm); | |
| if (threshold < 0.0f || sim >= threshold) { | |
| scores_idx.emplace_back(sim, i); | |
| } | |
| } | |
| // Partial sort for top-k | |
| if (static_cast<int>(scores_idx.size()) > k) { | |
| std::partial_sort( | |
| scores_idx.begin(), | |
| scores_idx.begin() + k, | |
| scores_idx.end(), | |
| [](const auto& a, const auto& b) { return a.first > b.first; } | |
| ); | |
| scores_idx.resize(k); | |
| } else { | |
| std::sort( | |
| scores_idx.begin(), | |
| scores_idx.end(), | |
| [](const auto& a, const auto& b) { return a.first > b.first; } | |
| ); | |
| } | |
| SearchResult result; | |
| result.indices.reserve(scores_idx.size()); | |
| result.scores.reserve(scores_idx.size()); | |
| for (const auto& [score, idx] : scores_idx) { | |
| result.indices.push_back(idx); | |
| result.scores.push_back(score); | |
| } | |
| return result; | |
| } | |
| // ───────────────────────────────────────────────── | |
| // Cache Lookup: find best match above threshold, return (index, score, hit) | |
| // ───────────────────────────────────────────────── | |
| struct CacheLookupResult { | |
| int best_index; | |
| float best_score; | |
| bool hit; | |
| }; | |
| CacheLookupResult cache_lookup( | |
| py::array_t<float> query, | |
| py::array_t<float> cache_matrix, | |
| float similarity_threshold | |
| ) { | |
| auto q_buf = query.request(); | |
| auto c_buf = cache_matrix.request(); | |
| if (q_buf.ndim != 1) | |
| throw std::runtime_error("Query must be 1-dimensional"); | |
| // Empty cache | |
| if (c_buf.ndim == 1 && c_buf.shape[0] == 0) { | |
| return {-1, 0.0f, false}; | |
| } | |
| if (c_buf.ndim != 2) | |
| throw std::runtime_error("Cache matrix must be 2-dimensional or empty 1-d"); | |
| if (q_buf.shape[0] != c_buf.shape[1]) | |
| throw std::runtime_error("Query dim must match cache matrix column dim"); | |
| int dim = static_cast<int>(q_buf.shape[0]); | |
| int num_entries = static_cast<int>(c_buf.shape[0]); | |
| const float* q_ptr = static_cast<const float*>(q_buf.ptr); | |
| const float* c_ptr = static_cast<const float*>(c_buf.ptr); | |
| float q_norm = avx2_l2_norm(q_ptr, dim); | |
| if (q_norm < 1e-9f) return {-1, 0.0f, false}; | |
| float best_score = -2.0f; | |
| int best_idx = -1; | |
| for (int i = 0; i < num_entries; i++) { | |
| const float* v_ptr = c_ptr + (static_cast<size_t>(i) * dim); | |
| float dot = avx2_dot_product(q_ptr, v_ptr, dim); | |
| float v_norm = avx2_l2_norm(v_ptr, dim); | |
| float sim = (v_norm < 1e-9f) ? 0.0f : dot / (q_norm * v_norm); | |
| if (sim > best_score) { | |
| best_score = sim; | |
| best_idx = i; | |
| } | |
| } | |
| bool hit = (best_score >= similarity_threshold); | |
| return {best_idx, best_score, hit}; | |
| } | |
| // ───────────────────────────────────────────────── | |
| // Batch Cosine Similarities: query vs matrix | |
| // ───────────────────────────────────────────────── | |
| py::array_t<float> batch_cosine_similarities( | |
| py::array_t<float> query, | |
| py::array_t<float> matrix | |
| ) { | |
| auto q_buf = query.request(); | |
| auto m_buf = matrix.request(); | |
| if (q_buf.ndim != 1) throw std::runtime_error("Query must be 1-d"); | |
| if (m_buf.ndim != 2) throw std::runtime_error("Matrix must be 2-d"); | |
| if (q_buf.shape[0] != m_buf.shape[1]) | |
| throw std::runtime_error("Dimension mismatch"); | |
| int dim = static_cast<int>(q_buf.shape[0]); | |
| int n = static_cast<int>(m_buf.shape[0]); | |
| const float* q_ptr = static_cast<const float*>(q_buf.ptr); | |
| const float* m_ptr = static_cast<const float*>(m_buf.ptr); | |
| float q_norm = avx2_l2_norm(q_ptr, dim); | |
| auto result = py::array_t<float>(n); | |
| auto r_buf = result.request(); | |
| float* r_ptr = static_cast<float*>(r_buf.ptr); | |
| for (int i = 0; i < n; i++) { | |
| const float* v_ptr = m_ptr + (static_cast<size_t>(i) * dim); | |
| float dot = avx2_dot_product(q_ptr, v_ptr, dim); | |
| float v_norm = avx2_l2_norm(v_ptr, dim); | |
| r_ptr[i] = (q_norm < 1e-9f || v_norm < 1e-9f) ? 0.0f : dot / (q_norm * v_norm); | |
| } | |
| return result; | |
| } | |
| // ───────────────────────────────────────────────── | |
| // Benchmarking utilities | |
| // ───────────────────────────────────────────────── | |
| double benchmark_search_latency_us( | |
| py::array_t<float> query, | |
| py::array_t<float> matrix, | |
| int k, | |
| int iterations = 1000 | |
| ) { | |
| std::vector<double> latencies; | |
| latencies.reserve(iterations); | |
| for (int i = 0; i < iterations; i++) { | |
| auto start = std::chrono::high_resolution_clock::now(); | |
| top_k_search(query, matrix, k); | |
| auto end = std::chrono::high_resolution_clock::now(); | |
| double us = std::chrono::duration<double, std::micro>(end - start).count(); | |
| latencies.push_back(us); | |
| } | |
| std::sort(latencies.begin(), latencies.end()); | |
| // Return p50 | |
| return latencies[iterations / 2]; | |
| } | |
| struct LatencyStats { | |
| double p50; | |
| double p95; | |
| double p99; | |
| double mean; | |
| double min_val; | |
| double max_val; | |
| }; | |
| LatencyStats benchmark_search_latency_full( | |
| py::array_t<float> query, | |
| py::array_t<float> matrix, | |
| int k, | |
| int iterations = 1000 | |
| ) { | |
| std::vector<double> latencies; | |
| latencies.reserve(iterations); | |
| for (int i = 0; i < iterations; i++) { | |
| auto start = std::chrono::high_resolution_clock::now(); | |
| top_k_search(query, matrix, k); | |
| auto end = std::chrono::high_resolution_clock::now(); | |
| double us = std::chrono::duration<double, std::micro>(end - start).count(); | |
| latencies.push_back(us); | |
| } | |
| std::sort(latencies.begin(), latencies.end()); | |
| int n = static_cast<int>(latencies.size()); | |
| double sum = std::accumulate(latencies.begin(), latencies.end(), 0.0); | |
| return { | |
| latencies[n / 2], // p50 | |
| latencies[static_cast<int>(n * 0.95)], // p95 | |
| latencies[static_cast<int>(n * 0.99)], // p99 | |
| sum / n, // mean | |
| latencies[0], // min | |
| latencies[n - 1] // max | |
| }; | |
| } | |
| // ───────────────────────────────────────────────── | |
| // Module: is_simd_available helper | |
| // ───────────────────────────────────────────────── | |
| bool is_simd_available() { | |
| return SIMD_AVAILABLE; | |
| } | |
| // ───────────────────────────────────────────────── | |
| // Pybind11 Module Definition | |
| // ───────────────────────────────────────────────── | |
| PYBIND11_MODULE(simd_engine, m) { | |
| m.doc() = "C++17 AVX2 + FMA SIMD Vector Search Engine for SentinelAI-RAG"; | |
| m.def("cosine_similarity", &cosine_similarity, | |
| py::arg("a"), py::arg("b"), | |
| "Compute cosine similarity between two vectors using AVX2+FMA"); | |
| py::class_<SearchResult>(m, "SearchResult") | |
| .def_readonly("indices", &SearchResult::indices) | |
| .def_readonly("scores", &SearchResult::scores); | |
| m.def("top_k_search", &top_k_search, | |
| py::arg("query"), py::arg("matrix"), py::arg("k"), | |
| py::arg("threshold") = -1.0f, | |
| "AVX2-accelerated top-k cosine similarity search"); | |
| py::class_<CacheLookupResult>(m, "CacheLookupResult") | |
| .def_readonly("best_index", &CacheLookupResult::best_index) | |
| .def_readonly("best_score", &CacheLookupResult::best_score) | |
| .def_readonly("hit", &CacheLookupResult::hit); | |
| m.def("cache_lookup", &cache_lookup, | |
| py::arg("query"), py::arg("cache_matrix"), py::arg("similarity_threshold"), | |
| "AVX2-accelerated cache lookup with similarity threshold"); | |
| m.def("batch_cosine_similarities", &batch_cosine_similarities, | |
| py::arg("query"), py::arg("matrix"), | |
| "Compute cosine similarity of query against all rows in matrix"); | |
| m.def("benchmark_search_latency_us", &benchmark_search_latency_us, | |
| py::arg("query"), py::arg("matrix"), py::arg("k"), | |
| py::arg("iterations") = 1000, | |
| "Benchmark top-k search latency, returns p50 in microseconds"); | |
| py::class_<LatencyStats>(m, "LatencyStats") | |
| .def_readonly("p50", &LatencyStats::p50) | |
| .def_readonly("p95", &LatencyStats::p95) | |
| .def_readonly("p99", &LatencyStats::p99) | |
| .def_readonly("mean", &LatencyStats::mean) | |
| .def_readonly("min_val", &LatencyStats::min_val) | |
| .def_readonly("max_val", &LatencyStats::max_val); | |
| m.def("benchmark_search_latency_full", &benchmark_search_latency_full, | |
| py::arg("query"), py::arg("matrix"), py::arg("k"), | |
| py::arg("iterations") = 1000, | |
| "Full latency benchmark returning p50/p95/p99/mean/min/max in µs"); | |
| m.def("is_simd_available", &is_simd_available, | |
| "Check if AVX2 SIMD instructions are available at compile time"); | |
| } | |