//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file contains the definition of the GpuMathTest class, a test harness /// that orchestrates running a math function on a device (GPU) and verifying /// its results. /// //===----------------------------------------------------------------------===// #ifndef MATHTEST_GPUMATHTEST_HPP #define MATHTEST_GPUMATHTEST_HPP #include "mathtest/DeviceContext.hpp" #include "mathtest/DeviceResources.hpp" #include "mathtest/HostRefChecker.hpp" #include "mathtest/InputGenerator.hpp" #include "mathtest/Support.hpp" #include "mathtest/TestResult.hpp" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include #include #include #include #include #include #include namespace mathtest { template > class [[nodiscard]] GpuMathTest final { using FunctionTraits = FunctionTraits; using OutType = typename FunctionTraits::ReturnType; using InTypesTuple = typename FunctionTraits::ArgTypesTuple; template using PartialResultType = TestResult; using KernelSignature = KernelSignatureOf_t; template using TypeIdentitiesTuple = std::tuple...>; using InTypeIdentitiesTuple = ApplyTupleTypes_t; static constexpr std::size_t DefaultBufferSize = DefaultBufferSizeFor_v; static constexpr uint32_t DefaultGroupSize = 512; public: using FunctionConfig = FunctionConfig; using ResultType = ApplyTupleTypes_t; using GeneratorType = ApplyTupleTypes_t; [[nodiscard]] static llvm::Expected create(std::shared_ptr Context, llvm::StringRef Provider, llvm::StringRef DeviceBinaryDir) { assert(Context && "Context must not be null"); auto ExpectedKernel = getKernel(*Context, Provider, DeviceBinaryDir); if (!ExpectedKernel) return ExpectedKernel.takeError(); return GpuMathTest(std::move(Context), Provider, *ExpectedKernel); } ResultType run(GeneratorType &Generator, std::size_t BufferSize = DefaultBufferSize, uint32_t GroupSize = DefaultGroupSize) const noexcept { assert(BufferSize > 0 && "Buffer size must be a positive value"); assert(GroupSize > 0 && "Group size must be a positive value"); auto [InBuffersTuple, OutBuffer] = createBuffers(BufferSize); ResultType FinalResult; while (true) { const std::size_t BatchSize = std::apply( [&](auto &...Buffers) { return Generator.fill(Buffers...); }, InBuffersTuple); if (BatchSize == 0) break; const auto BatchResult = processBatch(InBuffersTuple, OutBuffer, BatchSize, GroupSize); FinalResult.accumulate(BatchResult); } return FinalResult; } [[nodiscard]] std::shared_ptr getContext() const noexcept { return Context; } [[nodiscard]] std::string getProvider() const noexcept { return Provider; } private: explicit GpuMathTest(std::shared_ptr Context, llvm::StringRef Provider, DeviceKernel Kernel) : Context(std::move(Context)), Provider(Provider), Kernel(Kernel) {} static llvm::Expected> getKernel(const DeviceContext &Context, llvm::StringRef Provider, llvm::StringRef DeviceBinaryDir) { llvm::StringRef BinaryName = Provider; auto ExpectedImage = Context.loadBinary(DeviceBinaryDir, BinaryName); if (!ExpectedImage) return ExpectedImage.takeError(); auto ExpectedKernel = Context.getKernel( *ExpectedImage, FunctionConfig::KernelName); if (!ExpectedKernel) return ExpectedKernel.takeError(); return *ExpectedKernel; } [[nodiscard]] auto createBuffers(std::size_t BufferSize) const { auto InBuffersTuple = std::apply( [&](auto... InTypeIdentities) { return std::make_tuple( Context->createManagedBuffer< typename decltype(InTypeIdentities)::type>(BufferSize)...); }, InTypeIdentitiesTuple{}); auto OutBuffer = Context->createManagedBuffer(BufferSize); return std::make_pair(std::move(InBuffersTuple), std::move(OutBuffer)); } template [[nodiscard]] ResultType processBatch(const InBuffersTupleType &InBuffersTuple, ManagedBuffer &OutBuffer, std::size_t BatchSize, uint32_t GroupSize) const noexcept { const uint32_t NumGroups = (BatchSize + GroupSize - 1) / GroupSize; const auto KernelArgsTuple = std::apply( [&](const auto &...InBuffers) { return std::make_tuple(InBuffers.data()..., OutBuffer.data(), BatchSize); }, InBuffersTuple); std::apply( [&](const auto &...KernelArgs) { Context->launchKernel(Kernel, NumGroups, GroupSize, KernelArgs...); }, KernelArgsTuple); return check(InBuffersTuple, OutBuffer, BatchSize); } template [[nodiscard]] static ResultType check(const InBuffersTupleType &InBuffersTuple, const ManagedBuffer &OutBuffer, std::size_t BatchSize) noexcept { const auto InViewsTuple = std::apply( [&](auto &...InBuffers) { return std::make_tuple( llvm::ArrayRef(InBuffers.data(), BatchSize)...); }, InBuffersTuple); const auto OutView = llvm::ArrayRef(OutBuffer.data(), BatchSize); return Checker::check(InViewsTuple, OutView); } std::shared_ptr Context; std::string Provider; DeviceKernel Kernel; }; } // namespace mathtest #endif // MATHTEST_GPUMATHTEST_HPP