Files
TheRock/rocm-systems/emulation/rocjitsu/tests/sparse_memory_test.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

155 lines
5.0 KiB
C++

// Copyright (c) 2026 Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT
#include "simdojo/components/sparse_memory.h"
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <barrier>
#include <cstdint>
#include <cstring>
#include <span>
#include <thread>
#include <vector>
namespace {
TEST(SparseMemoryThreadingTest, ConcurrentDifferentPageWritesArePreserved) {
simdojo::SparseMemory memory("memory");
constexpr uint32_t kThreads = 8;
constexpr uint32_t kPagesPerThread = 64;
constexpr uint64_t kBase = 0x800000;
std::barrier start(kThreads);
std::vector<std::thread> workers;
workers.reserve(kThreads);
for (uint32_t tid = 0; tid < kThreads; ++tid) {
workers.emplace_back([&, tid] {
std::array<uint8_t, simdojo::SparseMemory::PAGE_SIZE> page{};
start.arrive_and_wait();
for (uint32_t i = 0; i < kPagesPerThread; ++i) {
const uint64_t addr =
kBase + (static_cast<uint64_t>(i) * kThreads + tid) * simdojo::SparseMemory::PAGE_SIZE;
for (uint32_t b = 0; b < page.size(); ++b)
page[b] = static_cast<uint8_t>((tid << 4) ^ i ^ b);
memory.write_block(addr, page);
}
});
}
for (auto &worker : workers)
worker.join();
EXPECT_EQ(memory.num_pages(), kThreads * kPagesPerThread);
std::array<uint8_t, simdojo::SparseMemory::PAGE_SIZE> expected{};
std::array<uint8_t, simdojo::SparseMemory::PAGE_SIZE> actual{};
for (uint32_t tid = 0; tid < kThreads; ++tid) {
for (uint32_t i = 0; i < kPagesPerThread; ++i) {
const uint64_t addr =
kBase + (static_cast<uint64_t>(i) * kThreads + tid) * simdojo::SparseMemory::PAGE_SIZE;
for (uint32_t b = 0; b < expected.size(); ++b)
expected[b] = static_cast<uint8_t>((tid << 4) ^ i ^ b);
memory.read_block(addr, actual);
EXPECT_EQ(actual, expected) << "addr=0x" << std::hex << addr;
uint32_t expected_word = 0;
std::memcpy(&expected_word, expected.data(), sizeof(expected_word));
EXPECT_EQ(memory.read32(addr), expected_word);
}
}
}
TEST(SparseMemoryThreadingTest, ConcurrentSamePageWritesArePreserved) {
simdojo::SparseMemory memory("memory");
constexpr uint32_t kThreads = 8;
constexpr size_t kBytesPerThread = simdojo::SparseMemory::PAGE_SIZE / kThreads;
constexpr uint64_t kBase = 0x900000;
std::barrier start(kThreads);
std::vector<std::thread> workers;
workers.reserve(kThreads);
for (uint32_t tid = 0; tid < kThreads; ++tid) {
workers.emplace_back([&, tid] {
std::array<uint8_t, kBytesPerThread> bytes{};
std::fill(bytes.begin(), bytes.end(), static_cast<uint8_t>(0x20 + tid));
start.arrive_and_wait();
for (uint32_t iteration = 0; iteration < 64; ++iteration)
memory.write_block(kBase + tid * kBytesPerThread, bytes);
});
}
for (auto &worker : workers)
worker.join();
std::array<uint8_t, simdojo::SparseMemory::PAGE_SIZE> actual{};
memory.read_block(kBase, actual);
for (uint32_t tid = 0; tid < kThreads; ++tid) {
const auto begin = actual.begin() + tid * kBytesPerThread;
const auto end = begin + kBytesPerThread;
EXPECT_TRUE(std::all_of(
begin, end, [tid](uint8_t byte) { return byte == static_cast<uint8_t>(0x20 + tid); }));
}
}
TEST(SparseMemoryTest, UnalignedBlockRoundTripCrossesPageBoundary) {
simdojo::SparseMemory memory("memory");
constexpr uint64_t kAddr = simdojo::SparseMemory::PAGE_SIZE - 37;
std::array<uint8_t, 128> input{};
for (size_t i = 0; i < input.size(); ++i)
input[i] = static_cast<uint8_t>((i * 17) ^ 0xA5);
memory.write_block(kAddr, input);
std::array<uint8_t, input.size()> output{};
memory.read_block(kAddr, output);
EXPECT_EQ(output, input);
EXPECT_EQ(memory.num_pages(), 2u);
}
TEST(SparseMemoryThreadingTest, ConcurrentOverlappingBlocksRemainAtomicPerPage) {
simdojo::SparseMemory memory("memory");
constexpr uint64_t kBase = 0xA00000;
constexpr size_t kBlockSize = 2 * simdojo::SparseMemory::PAGE_SIZE;
std::array<uint8_t, kBlockSize> first{};
std::array<uint8_t, kBlockSize> second{};
first.fill(0x3C);
second.fill(0xC3);
std::barrier start(2);
std::thread first_writer([&] {
start.arrive_and_wait();
for (uint32_t iteration = 0; iteration < 128; ++iteration)
memory.write_block(kBase, first);
});
std::thread second_writer([&] {
start.arrive_and_wait();
for (uint32_t iteration = 0; iteration < 128; ++iteration)
memory.write_block(kBase, second);
});
first_writer.join();
second_writer.join();
std::array<uint8_t, kBlockSize> actual{};
memory.read_block(kBase, actual);
for (size_t page = 0; page < 2; ++page) {
const auto bytes = std::span<const uint8_t>(actual).subspan(
page * simdojo::SparseMemory::PAGE_SIZE, simdojo::SparseMemory::PAGE_SIZE);
const bool is_first =
std::all_of(bytes.begin(), bytes.end(), [](uint8_t byte) { return byte == 0x3C; });
const bool is_second =
std::all_of(bytes.begin(), bytes.end(), [](uint8_t byte) { return byte == 0xC3; });
EXPECT_TRUE(is_first || is_second) << "page=" << page;
}
}
} // namespace