| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com> | |
| // | |
| // This Source Code Form is subject to the terms of the Mozilla | |
| // Public License v. 2.0. If a copy of the MPL was not distributed | |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| // Device-resident scalar and element-wise operations built on the NPP signal | |
| // primitives (CUDA::npps), which keeps the module header-only: no custom CUDA | |
| // kernels to compile. | |
| #ifndef EIGEN_GPU_DEVICE_SCALAR_OPS_H | |
| #define EIGEN_GPU_DEVICE_SCALAR_OPS_H | |
| #include <cuda_runtime.h> | |
| #include <npps_arithmetic_and_logical_operations.h> | |
| #include "./GpuSupport.h" | |
| namespace Eigen { | |
| namespace gpu { | |
| namespace internal { | |
| // NPP requires nCudaDeviceId and the device attributes to match the device that | |
| // owns `stream`. Re-querying per call is cheap next to the NPP launch itself and | |
| // keeps multi-device and borrowed-stream callers correct. | |
| inline NppStreamContext make_npp_stream_ctx(cudaStream_t stream) { | |
| NppStreamContext ctx = {}; | |
| ctx.hStream = stream; | |
| #if CUDART_VERSION >= 12080 | |
| // cudaStreamGetDevice reports the stream's owning device irrespective of the | |
| // calling thread's current device. | |
| EIGEN_CUDA_RUNTIME_CHECK(cudaStreamGetDevice(stream, &ctx.nCudaDeviceId)); | |
| #else | |
| // Without cudaStreamGetDevice (pre-CUDA 12.8), a caller borrowing a stream from | |
| // another device must cudaSetDevice() first. | |
| EIGEN_CUDA_RUNTIME_CHECK(cudaGetDevice(&ctx.nCudaDeviceId)); | |
| #endif | |
| EIGEN_CUDA_RUNTIME_CHECK(cudaDeviceGetAttribute(&ctx.nCudaDevAttrComputeCapabilityMajor, | |
| cudaDevAttrComputeCapabilityMajor, ctx.nCudaDeviceId)); | |
| EIGEN_CUDA_RUNTIME_CHECK(cudaDeviceGetAttribute(&ctx.nCudaDevAttrComputeCapabilityMinor, | |
| cudaDevAttrComputeCapabilityMinor, ctx.nCudaDeviceId)); | |
| EIGEN_CUDA_RUNTIME_CHECK( | |
| cudaDeviceGetAttribute(&ctx.nMultiProcessorCount, cudaDevAttrMultiProcessorCount, ctx.nCudaDeviceId)); | |
| EIGEN_CUDA_RUNTIME_CHECK(cudaDeviceGetAttribute(&ctx.nMaxThreadsPerMultiProcessor, | |
| cudaDevAttrMaxThreadsPerMultiProcessor, ctx.nCudaDeviceId)); | |
| EIGEN_CUDA_RUNTIME_CHECK( | |
| cudaDeviceGetAttribute(&ctx.nMaxThreadsPerBlock, cudaDevAttrMaxThreadsPerBlock, ctx.nCudaDeviceId)); | |
| int shared_mem_per_block = 0; | |
| EIGEN_CUDA_RUNTIME_CHECK( | |
| cudaDeviceGetAttribute(&shared_mem_per_block, cudaDevAttrMaxSharedMemoryPerBlock, ctx.nCudaDeviceId)); | |
| ctx.nSharedMemPerBlock = static_cast<size_t>(shared_mem_per_block); | |
| EIGEN_CUDA_RUNTIME_CHECK(cudaStreamGetFlags(stream, &ctx.nStreamFlags)); | |
| return ctx; | |
| } | |
| // c = a / b. The operands are swapped because NPP computes | |
| // pDst[i] = pSrc2[i] / pSrc1[i]. | |
| inline void device_scalar_div(const float* a, const float* b, float* c, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsDiv_32f_Ctx(b, a, c, 1, npp_ctx); | |
| } | |
| inline void device_scalar_div(const double* a, const double* b, double* c, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsDiv_64f_Ctx(b, a, c, 1, npp_ctx); | |
| } | |
| // c = -a. | |
| inline void device_scalar_neg(const float* a, float* c, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsMulC_32f_Ctx(a, -1.0f, c, 1, npp_ctx); | |
| } | |
| inline void device_scalar_neg(const double* a, double* c, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsMulC_64f_Ctx(a, -1.0, c, 1, npp_ctx); | |
| } | |
| // c[i] = a[i] * b[i]. | |
| inline void device_cwiseProduct(const float* a, const float* b, float* c, int n, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsMul_32f_Ctx(a, b, c, static_cast<size_t>(n), npp_ctx); | |
| } | |
| inline void device_cwiseProduct(const double* a, const double* b, double* c, int n, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsMul_64f_Ctx(a, b, c, static_cast<size_t>(n), npp_ctx); | |
| } | |
| // c[i] = a[i] / b[i], with operands swapped as in device_scalar_div. | |
| inline void device_cwiseQuotient(const float* a, const float* b, float* c, int n, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsDiv_32f_Ctx(b, a, c, static_cast<size_t>(n), npp_ctx); | |
| } | |
| inline void device_cwiseQuotient(const double* a, const double* b, double* c, int n, cudaStream_t stream) { | |
| NppStreamContext npp_ctx = make_npp_stream_ctx(stream); | |
| nppsDiv_64f_Ctx(b, a, c, static_cast<size_t>(n), npp_ctx); | |
| } | |
| } // namespace internal | |
| } // namespace gpu | |
| } // namespace Eigen | |
| #endif // EIGEN_GPU_DEVICE_SCALAR_OPS_H |