|
| 1 | +//==--- KPCache.cpp --- KernelProgramCache for multiple devices unit test --==// |
| 2 | +// |
| 3 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | +// See https://llvm.org/LICENSE.txt for license information. |
| 5 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | +// |
| 7 | +//===----------------------------------------------------------------------===// |
| 8 | + |
| 9 | +#define SYCL2020_DISABLE_DEPRECATION_WARNINGS |
| 10 | + |
| 11 | +#include "detail/context_impl.hpp" |
| 12 | +#include "detail/kernel_bundle_impl.hpp" |
| 13 | +#include "detail/kernel_program_cache.hpp" |
| 14 | +#include <helpers/CommonRedefinitions.hpp> |
| 15 | +#include <helpers/PiImage.hpp> |
| 16 | +#include <helpers/PiMock.hpp> |
| 17 | +#include <helpers/TestKernel.hpp> |
| 18 | + |
| 19 | +#include <gtest/gtest.h> |
| 20 | + |
| 21 | +#include <iostream> |
| 22 | + |
| 23 | +using namespace sycl; |
| 24 | + |
| 25 | +static pi_result redefinedContextCreate( |
| 26 | + const pi_context_properties *properties, pi_uint32 num_devices, |
| 27 | + const pi_device *devices, |
| 28 | + void (*pfn_notify)(const char *errinfo, const void *private_info, size_t cb, |
| 29 | + void *user_data), |
| 30 | + void *user_data, pi_context *ret_context) { |
| 31 | + *ret_context = reinterpret_cast<pi_context>(123); |
| 32 | + return PI_SUCCESS; |
| 33 | +} |
| 34 | + |
| 35 | +static pi_result redefinedContextRelease(pi_context context) { |
| 36 | + return PI_SUCCESS; |
| 37 | +} |
| 38 | + |
| 39 | +static pi_result redefinedDevicesGet(pi_platform platform, |
| 40 | + pi_device_type device_type, |
| 41 | + pi_uint32 num_entries, pi_device *devices, |
| 42 | + pi_uint32 *num_devices) { |
| 43 | + if (num_devices) { |
| 44 | + *num_devices = static_cast<pi_uint32>(2); |
| 45 | + return PI_SUCCESS; |
| 46 | + } |
| 47 | + |
| 48 | + if (num_entries == 2 && devices) { |
| 49 | + devices[0] = reinterpret_cast<pi_device>(1111); |
| 50 | + devices[1] = reinterpret_cast<pi_device>(2222); |
| 51 | + } |
| 52 | + return PI_SUCCESS; |
| 53 | +} |
| 54 | + |
| 55 | +static pi_result redefinedDeviceGetInfo(pi_device device, |
| 56 | + pi_device_info param_name, |
| 57 | + size_t param_value_size, |
| 58 | + void *param_value, |
| 59 | + size_t *param_value_size_ret) { |
| 60 | + if (param_name == PI_DEVICE_INFO_TYPE) { |
| 61 | + auto *Result = reinterpret_cast<_pi_device_type *>(param_value); |
| 62 | + *Result = PI_DEVICE_TYPE_GPU; |
| 63 | + } |
| 64 | + if (param_name == PI_DEVICE_INFO_COMPILER_AVAILABLE) { |
| 65 | + auto *Result = reinterpret_cast<pi_bool *>(param_value); |
| 66 | + *Result = true; |
| 67 | + } |
| 68 | + return PI_SUCCESS; |
| 69 | +} |
| 70 | + |
| 71 | +static pi_result redefinedDeviceRetain(pi_device device) { return PI_SUCCESS; } |
| 72 | + |
| 73 | +static pi_result redefinedDeviceRelease(pi_device device) { return PI_SUCCESS; } |
| 74 | + |
| 75 | +static pi_result redefinedQueueCreate(pi_context context, pi_device device, |
| 76 | + pi_queue_properties properties, |
| 77 | + pi_queue *queue) { |
| 78 | + *queue = reinterpret_cast<pi_queue>(1234); |
| 79 | + return PI_SUCCESS; |
| 80 | +} |
| 81 | + |
| 82 | +static pi_result redefinedQueueRelease(pi_queue command_queue) { |
| 83 | + return PI_SUCCESS; |
| 84 | +} |
| 85 | + |
| 86 | +static size_t ProgramNum = 12345; |
| 87 | +static pi_result redefinedProgramCreate(pi_context context, const void *il, |
| 88 | + size_t length, |
| 89 | + pi_program *res_program) { |
| 90 | + size_t CurrentProgram = ProgramNum; |
| 91 | + *res_program = reinterpret_cast<pi_program>(CurrentProgram); |
| 92 | + ++ProgramNum; |
| 93 | + return PI_SUCCESS; |
| 94 | +} |
| 95 | + |
| 96 | +static int RetainCounter = 0; |
| 97 | +static pi_result redefinedProgramRetain(pi_program program) { |
| 98 | + ++RetainCounter; |
| 99 | + return PI_SUCCESS; |
| 100 | +} |
| 101 | + |
| 102 | +static int KernelReleaseCounter = 0; |
| 103 | +static pi_result redefinedKernelRelease(pi_kernel kernel) { |
| 104 | + ++KernelReleaseCounter; |
| 105 | + return PI_SUCCESS; |
| 106 | +} |
| 107 | + |
| 108 | +class MultipleDeviceCacheTest : public ::testing::Test { |
| 109 | +public: |
| 110 | + MultipleDeviceCacheTest() : Plt{default_selector()} {} |
| 111 | + |
| 112 | +protected: |
| 113 | + void SetUp() override { |
| 114 | + if (Plt.is_host() || Plt.get_backend() != backend::opencl) { |
| 115 | + return; |
| 116 | + } |
| 117 | + |
| 118 | + Mock = std::make_unique<unittest::PiMock>(Plt); |
| 119 | + |
| 120 | + setupDefaultMockAPIs(*Mock); |
| 121 | + Mock->redefine<detail::PiApiKind::piDevicesGet>(redefinedDevicesGet); |
| 122 | + Mock->redefine<detail::PiApiKind::piDeviceGetInfo>(redefinedDeviceGetInfo); |
| 123 | + Mock->redefine<detail::PiApiKind::piDeviceRetain>(redefinedDeviceRetain); |
| 124 | + Mock->redefine<detail::PiApiKind::piDeviceRelease>(redefinedDeviceRelease); |
| 125 | + Mock->redefine<detail::PiApiKind::piContextCreate>(redefinedContextCreate); |
| 126 | + Mock->redefine<detail::PiApiKind::piContextRelease>( |
| 127 | + redefinedContextRelease); |
| 128 | + Mock->redefine<detail::PiApiKind::piQueueCreate>(redefinedQueueCreate); |
| 129 | + Mock->redefine<detail::PiApiKind::piQueueRelease>(redefinedQueueRelease); |
| 130 | + Mock->redefine<detail::PiApiKind::piProgramRetain>(redefinedProgramRetain); |
| 131 | + Mock->redefine<detail::PiApiKind::piProgramCreate>(redefinedProgramCreate); |
| 132 | + Mock->redefine<detail::PiApiKind::piKernelRelease>(redefinedKernelRelease); |
| 133 | + } |
| 134 | + |
| 135 | +protected: |
| 136 | + std::unique_ptr<unittest::PiMock> Mock; |
| 137 | + platform Plt; |
| 138 | +}; |
| 139 | + |
| 140 | +// Test that program is retained for each device and each kernel is released |
| 141 | +// once |
| 142 | +TEST_F(MultipleDeviceCacheTest, ProgramRetain) { |
| 143 | + if (Plt.is_host() || Plt.get_backend() != backend::opencl) { |
| 144 | + return; |
| 145 | + } |
| 146 | + { |
| 147 | + std::vector<sycl::device> Devices = Plt.get_devices(info::device_type::gpu); |
| 148 | + sycl::context Context(Devices); |
| 149 | + sycl::queue Queue(Context, Devices[0]); |
| 150 | + assert(Devices.size() == 2); |
| 151 | + |
| 152 | + auto Bundle = cl::sycl::get_kernel_bundle<sycl::bundle_state::input>( |
| 153 | + Queue.get_context()); |
| 154 | + Queue.submit( |
| 155 | + [&](cl::sycl::handler &cgh) { cgh.single_task<TestKernel>([]() {}); }); |
| 156 | + |
| 157 | + auto BundleObject = cl::sycl::build(Bundle, Bundle.get_devices()); |
| 158 | + auto KernelID = cl::sycl::get_kernel_id<TestKernel>(); |
| 159 | + auto Kernel = BundleObject.get_kernel(KernelID); |
| 160 | + |
| 161 | + // Because of emulating 2 devices program is retained for each one in |
| 162 | + // build(). It is also depends on number of device images. This test has one |
| 163 | + // image, but other tests can create other images. Additional variable is |
| 164 | + // added to control count of piProgramRetain calls |
| 165 | + auto BundleImpl = getSyclObjImpl(Bundle); |
| 166 | + int NumRetains = BundleImpl->size() * 2; |
| 167 | + |
| 168 | + EXPECT_EQ(RetainCounter, NumRetains) |
| 169 | + << "Expect " << NumRetains << " piProgramRetain calls"; |
| 170 | + |
| 171 | + auto CtxImpl = detail::getSyclObjImpl(Context); |
| 172 | + detail::KernelProgramCache::KernelCacheT &KernelCache = |
| 173 | + CtxImpl->getKernelProgramCache().acquireKernelsPerProgramCache().get(); |
| 174 | + |
| 175 | + EXPECT_EQ(KernelCache.size(), (size_t)2) << "Expect 2 kernels in cache"; |
| 176 | + } |
| 177 | + // First kernel creating is called in handler::single_task(). |
| 178 | + // kernel_bundle::get_kernel() creates a kernel and shares it with created |
| 179 | + // programs. Also the kernel is retained in kernel_bundle::get_kernel(). A |
| 180 | + // kernel is removed from cache if piKernelRelease was called for it, so it |
| 181 | + // will not be removed twice for the other programs. As a result we must |
| 182 | + // expect 3 piKernelRelease calls. |
| 183 | + EXPECT_EQ(KernelReleaseCounter, 3) << "Expect 3 piKernelRelease calls"; |
| 184 | +} |
0 commit comments