Adds olMemPrefetch and the OL_MEM_MIGRATION_FLAG_* enum, wired through a new dataPrefetchImpl on the plugin interface with Level Zero and CUDA implementations. Backends without prefetch support inherit the default no-op. Prefetch is a hint that migrates a USM allocation between the host and a device so subsequent accesses on the target side are faster. Assisted-by: Claude
150 lines
4.9 KiB
C++
150 lines
4.9 KiB
C++
//===------- Offload API tests - olMemPrefetch ----------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "../common/Fixtures.hpp"
|
|
#include <OffloadAPI.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
using olMemPrefetchTest = OffloadQueueTest;
|
|
OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE(olMemPrefetchTest);
|
|
|
|
TEST_P(olMemPrefetchTest, SuccessHostToDevice) {
|
|
constexpr size_t Size = 1024;
|
|
void *Alloc;
|
|
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, Size, &Alloc));
|
|
|
|
std::memset(Alloc, 0x42, Size);
|
|
|
|
const void *Mems[] = {Alloc};
|
|
const size_t Sizes[] = {Size};
|
|
ASSERT_SUCCESS(olMemPrefetch(Queue, 1, Mems, Sizes,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
ASSERT_SUCCESS(olSyncQueue(Queue));
|
|
|
|
for (size_t I = 0; I < Size; I++)
|
|
ASSERT_EQ(static_cast<uint8_t *>(Alloc)[I], 0x42);
|
|
|
|
ASSERT_SUCCESS(olMemFree(Alloc));
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, SuccessDeviceToHost) {
|
|
constexpr size_t Size = 1024;
|
|
void *Alloc;
|
|
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, Size, &Alloc));
|
|
|
|
std::memset(Alloc, 0x21, Size);
|
|
|
|
const void *Mems[] = {Alloc};
|
|
const size_t Sizes[] = {Size};
|
|
// Migrate to the device first, then bring it back.
|
|
ASSERT_SUCCESS(olMemPrefetch(Queue, 1, Mems, Sizes,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
ASSERT_SUCCESS(olMemPrefetch(Queue, 1, Mems, Sizes,
|
|
OL_MEM_MIGRATION_FLAG_DEVICE_TO_HOST));
|
|
ASSERT_SUCCESS(olSyncQueue(Queue));
|
|
|
|
for (size_t I = 0; I < Size; I++)
|
|
ASSERT_EQ(static_cast<uint8_t *>(Alloc)[I], 0x21);
|
|
|
|
ASSERT_SUCCESS(olMemFree(Alloc));
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, SuccessMultiple) {
|
|
constexpr size_t NumAllocs = 3;
|
|
constexpr size_t Size = 1024;
|
|
void *Allocs[NumAllocs];
|
|
const void *Mems[NumAllocs];
|
|
size_t Sizes[NumAllocs];
|
|
for (size_t I = 0; I < NumAllocs; I++) {
|
|
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, Size, &Allocs[I]));
|
|
std::memset(Allocs[I], static_cast<int>(0x10 + I), Size);
|
|
Mems[I] = Allocs[I];
|
|
Sizes[I] = Size;
|
|
}
|
|
|
|
ASSERT_SUCCESS(olMemPrefetch(Queue, NumAllocs, Mems, Sizes,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
ASSERT_SUCCESS(olSyncQueue(Queue));
|
|
|
|
for (size_t I = 0; I < NumAllocs; I++) {
|
|
for (size_t J = 0; J < Size; J++)
|
|
ASSERT_EQ(static_cast<uint8_t *>(Allocs[I])[J],
|
|
static_cast<uint8_t>(0x10 + I));
|
|
ASSERT_SUCCESS(olMemFree(Allocs[I]));
|
|
}
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, SuccessZeroCount) {
|
|
ASSERT_SUCCESS(olMemPrefetch(Queue, 0, nullptr, nullptr,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
ASSERT_SUCCESS(olSyncQueue(Queue));
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, SuccessZeroSize) {
|
|
constexpr size_t Size = 1024;
|
|
void *Alloc;
|
|
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, Size, &Alloc));
|
|
|
|
const void *Mems[] = {Alloc};
|
|
const size_t Sizes[] = {0};
|
|
ASSERT_SUCCESS(olMemPrefetch(Queue, 1, Mems, Sizes,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
ASSERT_SUCCESS(olSyncQueue(Queue));
|
|
|
|
ASSERT_SUCCESS(olMemFree(Alloc));
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, SuccessUnsupportedAllocType) {
|
|
// Prefetching a non-managed allocation is not meaningful, but per the API
|
|
// contract the hint must be silently ignored and the call must still succeed.
|
|
constexpr size_t Size = 1024;
|
|
void *Alloc;
|
|
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &Alloc));
|
|
|
|
const void *Mems[] = {Alloc};
|
|
const size_t Sizes[] = {Size};
|
|
ASSERT_SUCCESS(olMemPrefetch(Queue, 1, Mems, Sizes,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
ASSERT_SUCCESS(olSyncQueue(Queue));
|
|
|
|
ASSERT_SUCCESS(olMemFree(Alloc));
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, InvalidFlags) {
|
|
constexpr size_t Size = 1024;
|
|
void *Alloc;
|
|
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, Size, &Alloc));
|
|
|
|
const void *Mems[] = {Alloc};
|
|
const size_t Sizes[] = {Size};
|
|
ASSERT_ERROR(OL_ERRC_INVALID_ENUMERATION,
|
|
olMemPrefetch(Queue, 1, Mems, Sizes, 0xdeadbeef));
|
|
|
|
ASSERT_SUCCESS(olMemFree(Alloc));
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, InvalidNullMems) {
|
|
const size_t Sizes[] = {0};
|
|
ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER,
|
|
olMemPrefetch(Queue, 1, nullptr, Sizes,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
}
|
|
|
|
TEST_P(olMemPrefetchTest, InvalidNullSizes) {
|
|
constexpr size_t Size = 1024;
|
|
void *Alloc;
|
|
ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_MANAGED, Size, &Alloc));
|
|
|
|
const void *Mems[] = {Alloc};
|
|
ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER,
|
|
olMemPrefetch(Queue, 1, Mems, nullptr,
|
|
OL_MEM_MIGRATION_FLAG_HOST_TO_DEVICE));
|
|
|
|
ASSERT_SUCCESS(olMemFree(Alloc));
|
|
}
|