StagingBuffer: Large slots should be aligned to alignment
Slots whose stride size is larger than the slot alignment value should have their size rounded up to the alignment size so that the slots that follow them will also be aligned.
This commit is contained in:
@@ -68,10 +68,13 @@ static size_t calculateMaxAlignment(
|
|||||||
|
|
||||||
void StagingBuffer::computeSlotStrideAndBufferSize()
|
void StagingBuffer::computeSlotStrideAndBufferSize()
|
||||||
{
|
{
|
||||||
// Slot stride is the maximum of alignment and padding
|
// Slot stride is the maximum of alignment and padding, rounded up to a multiple of alignment
|
||||||
slotStrideNBytes = std::max(
|
size_t minSlotStride = std::max(
|
||||||
inputConstraints.slotStartAlignmentByteVal,
|
inputConstraints.slotStartAlignmentByteVal,
|
||||||
inputConstraints.slotPadToNBytes);
|
inputConstraints.slotPadToNBytes);
|
||||||
|
slotStrideNBytes = ((minSlotStride + inputConstraints.slotStartAlignmentByteVal - 1)
|
||||||
|
/ inputConstraints.slotStartAlignmentByteVal)
|
||||||
|
* inputConstraints.slotStartAlignmentByteVal;
|
||||||
|
|
||||||
// Calculate maximum alignment needed for first slot (must satisfy both frame and slot alignment)
|
// Calculate maximum alignment needed for first slot (must satisfy both frame and slot alignment)
|
||||||
size_t maxAlignment = calculateMaxAlignment(
|
size_t maxAlignment = calculateMaxAlignment(
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public:
|
|||||||
+ ": StimulusFrame: failed to create clBuffer");
|
+ ": StimulusFrame: failed to create clBuffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << __func__ << ": StimulusFrame: created clBuffer with size " << slotDesc.nBytes << " bytes @ " << (const void*)slotDesc.vaddr << std::endl;
|
// std::cout << __func__ << ": StimulusFrame: created clBuffer with size " << slotDesc.nBytes << " bytes @ " << (const void*)slotDesc.vaddr << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
~StimulusFrame() = default;
|
~StimulusFrame() = default;
|
||||||
|
|||||||
@@ -9,76 +9,6 @@
|
|||||||
namespace smo {
|
namespace smo {
|
||||||
namespace stim_buff {
|
namespace stim_buff {
|
||||||
|
|
||||||
// Helper function to calculate expected buffer size using the same logic as computeSlotStrideAndBufferSize
|
|
||||||
static size_t calculateExpectedBufferSize(
|
|
||||||
size_t nSlots,
|
|
||||||
const StagingBuffer::IOEngineConstraints& constraints)
|
|
||||||
{
|
|
||||||
// Slot stride is the maximum of alignment and padding
|
|
||||||
size_t slotStrideNBytes = std::max(
|
|
||||||
constraints.slotStartAlignmentByteVal,
|
|
||||||
constraints.slotPadToNBytes);
|
|
||||||
|
|
||||||
// Calculate maximum alignment needed for first slot
|
|
||||||
size_t maxAlignment;
|
|
||||||
if (constraints.frameStartAlignmentByteVal >= constraints.slotStartAlignmentByteVal)
|
|
||||||
{
|
|
||||||
if (constraints.frameStartAlignmentByteVal % constraints.slotStartAlignmentByteVal == 0)
|
|
||||||
{
|
|
||||||
maxAlignment = constraints.frameStartAlignmentByteVal;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
maxAlignment = std::max(
|
|
||||||
constraints.frameStartAlignmentByteVal,
|
|
||||||
constraints.slotStartAlignmentByteVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (constraints.slotStartAlignmentByteVal % constraints.frameStartAlignmentByteVal == 0)
|
|
||||||
{
|
|
||||||
maxAlignment = constraints.slotStartAlignmentByteVal;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
maxAlignment = std::max(
|
|
||||||
constraints.frameStartAlignmentByteVal,
|
|
||||||
constraints.slotStartAlignmentByteVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate minimum buffer size
|
|
||||||
size_t minBufferSize = std::max(
|
|
||||||
constraints.framePadToNBytes,
|
|
||||||
constraints.slotPadToNBytes);
|
|
||||||
|
|
||||||
// Calculate total size needed for nSlots slots
|
|
||||||
size_t slotAreaSize = nSlots * slotStrideNBytes;
|
|
||||||
|
|
||||||
// Add padding space at buffer start for alignment offset (worst case: max alignment - 1)
|
|
||||||
size_t alignmentPadding = maxAlignment - 1;
|
|
||||||
|
|
||||||
// Total size needed: alignment padding + slot area, then ensure minimum is met
|
|
||||||
size_t rawSize = alignmentPadding + slotAreaSize;
|
|
||||||
if (rawSize < minBufferSize)
|
|
||||||
{
|
|
||||||
rawSize = minBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Align up to the maximum alignment to ensure we can always find a valid offset
|
|
||||||
return ((rawSize + maxAlignment - 1) / maxAlignment) * maxAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to calculate expected slot stride
|
|
||||||
static size_t calculateExpectedSlotStride(
|
|
||||||
const StagingBuffer::IOEngineConstraints& constraints)
|
|
||||||
{
|
|
||||||
return std::max(
|
|
||||||
constraints.slotStartAlignmentByteVal,
|
|
||||||
constraints.slotPadToNBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to create test constraints
|
// Helper function to create test constraints
|
||||||
static StagingBuffer::IOEngineConstraints createTestConstraints(
|
static StagingBuffer::IOEngineConstraints createTestConstraints(
|
||||||
size_t slotStartAlignment,
|
size_t slotStartAlignment,
|
||||||
@@ -117,357 +47,230 @@ protected:
|
|||||||
size_t pageSize;
|
size_t pageSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test 1: Single slot, minimal padding, small alignments
|
// Helper function to verify all slots are page-aligned and have correct stride
|
||||||
TEST_F(StagingBufferTest, SingleSlotMinimalAlignment) {
|
static void verifyAllSlotsPageAligned(
|
||||||
size_t nSlots = 1;
|
const StagingBuffer& buffer,
|
||||||
auto constraints = createTestConstraints(4, 16, 4, 16);
|
size_t expectedSlotStride,
|
||||||
|
size_t pageSize)
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
{
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
|
|
||||||
// Verify FrameAssemblyDesc
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
ASSERT_NE(frameDesc, nullptr);
|
||||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
ASSERT_GT(frameDesc->slots.size(), 0u);
|
||||||
EXPECT_EQ(frameDesc->slots.size(), nSlots);
|
|
||||||
|
|
||||||
// Verify first slot alignment
|
// Verify slot stride is a multiple of page size
|
||||||
if (!frameDesc->slots.empty()) {
|
EXPECT_EQ(expectedSlotStride % pageSize, 0u)
|
||||||
verifyAlignment(frameDesc->slots[0].vaddr, constraints.slotStartAlignmentByteVal,
|
<< "Slot stride " << expectedSlotStride
|
||||||
"First slot");
|
<< " should be a multiple of page size " << pageSize;
|
||||||
verifyAlignment(frameDesc->slots[0].vaddr, constraints.frameStartAlignmentByteVal,
|
|
||||||
"First slot frame alignment");
|
// Verify first slot is page-aligned
|
||||||
|
verifyAlignment(frameDesc->slots[0].vaddr, pageSize, "First slot");
|
||||||
|
|
||||||
|
// Verify all subsequent slots are page-aligned and have correct stride
|
||||||
|
for (size_t i = 1; i < frameDesc->slots.size(); ++i) {
|
||||||
|
verifyAlignment(frameDesc->slots[i].vaddr, pageSize,
|
||||||
|
"Slot " + std::to_string(i));
|
||||||
|
|
||||||
|
// Verify actual stride matches expected stride
|
||||||
|
size_t actualStride = reinterpret_cast<uintptr_t>(frameDesc->slots[i].vaddr) -
|
||||||
|
reinterpret_cast<uintptr_t>(frameDesc->slots[i-1].vaddr);
|
||||||
|
EXPECT_EQ(actualStride, expectedSlotStride)
|
||||||
|
<< "Slot " << i << " stride mismatch: expected " << expectedSlotStride
|
||||||
|
<< ", got " << actualStride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 2: Multiple slots, no alignment padding needed
|
// Test 1: Small slot stride (< page size) - should round up to page size
|
||||||
TEST_F(StagingBufferTest, MultipleSlotsMinimalAlignment) {
|
TEST_F(StagingBufferTest, SmallSlotStrideRoundsUpToPageSize) {
|
||||||
size_t nSlots = 10;
|
size_t nSlots = 10;
|
||||||
auto constraints = createTestConstraints(4, 16, 4, 16);
|
size_t smallSlotPad = 256; // Much smaller than typical page size (4096)
|
||||||
|
auto constraints = createTestConstraints(
|
||||||
|
pageSize, // slotStartAlignment (page size)
|
||||||
|
smallSlotPad, // slotPadToNBytes (small)
|
||||||
|
pageSize, // frameStartAlignment
|
||||||
|
pageSize); // framePadToNBytes
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
// Slot stride should be rounded up to page size
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
size_t expectedSlotStride = pageSize;
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||||
|
<< "Small slot pad should round up to page size";
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
|
|
||||||
// Verify all slots are properly spaced
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < frameDesc->slots.size(); ++i) {
|
|
||||||
if (i > 0) {
|
|
||||||
size_t actualStride = reinterpret_cast<uintptr_t>(frameDesc->slots[i].vaddr) -
|
|
||||||
reinterpret_cast<uintptr_t>(frameDesc->slots[i-1].vaddr);
|
|
||||||
EXPECT_EQ(actualStride, expectedSlotStride)
|
|
||||||
<< "Slot " << i << " stride mismatch";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 3: OpenCL Mesh constraints (real-world scenario)
|
// Test 2: Slot stride equal to page size - should remain page size
|
||||||
TEST_F(StagingBufferTest, OpenClMeshConstraints) {
|
TEST_F(StagingBufferTest, SlotStrideEqualToPageSize) {
|
||||||
size_t nSlots = 909; // 30000ms / 33ms
|
size_t nSlots = 20;
|
||||||
size_t nDgramsPerFrame = 84;
|
|
||||||
size_t nPointsPerDgram = 96; // SingleFirst/Strongest/Dual return mode
|
|
||||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 3; // 96768
|
|
||||||
|
|
||||||
auto constraints = createTestConstraints(
|
auto constraints = createTestConstraints(
|
||||||
sizeof(float), // slotStartAlignment
|
pageSize, // slotStartAlignment
|
||||||
|
pageSize, // slotPadToNBytes (equal to page size)
|
||||||
|
pageSize, // frameStartAlignment
|
||||||
|
pageSize); // framePadToNBytes
|
||||||
|
|
||||||
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
|
|
||||||
|
size_t expectedSlotStride = pageSize;
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||||
|
<< "Slot stride equal to page size should remain unchanged";
|
||||||
|
|
||||||
|
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Slot stride slightly larger than page size (e.g., 336) - should round up
|
||||||
|
TEST_F(StagingBufferTest, SlotStrideSlightlyLargerThanPageSize) {
|
||||||
|
size_t nSlots = 50;
|
||||||
|
size_t slotPadToNBytes = 336; // Slightly larger than page size (4096)
|
||||||
|
auto constraints = createTestConstraints(
|
||||||
|
pageSize, // slotStartAlignment
|
||||||
slotPadToNBytes, // slotPadToNBytes
|
slotPadToNBytes, // slotPadToNBytes
|
||||||
pageSize, // frameStartAlignment
|
pageSize, // frameStartAlignment
|
||||||
pageSize); // framePadToNBytes
|
pageSize); // framePadToNBytes
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
// Should round up to next multiple of page size (4096)
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
size_t expectedSlotStride = pageSize;
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||||
|
<< "Slot stride 336 should round up to page size " << pageSize;
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
}
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
|
||||||
<< "Buffer size should be page-aligned";
|
|
||||||
|
|
||||||
// Verify FrameAssemblyDesc
|
// Test 4: Slot stride much larger than page size (e.g., 32256) - should round up to 32768
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
TEST_F(StagingBufferTest, SlotStrideMuchLargerThanPageSize) {
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
size_t nSlots = 100;
|
||||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
size_t slotPadToNBytes = 32256; // Much larger than page size
|
||||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
auto constraints = createTestConstraints(
|
||||||
|
pageSize, // slotStartAlignment
|
||||||
|
slotPadToNBytes, // slotPadToNBytes
|
||||||
|
pageSize, // frameStartAlignment
|
||||||
|
pageSize); // framePadToNBytes
|
||||||
|
|
||||||
// Verify first slot alignment
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
if (!frameDesc->slots.empty()) {
|
|
||||||
verifyAlignment(frameDesc->slots[0].vaddr, pageSize, "First slot frame alignment");
|
// Should round up to next multiple of page size
|
||||||
|
// 32256 / 4096 = 7.875, so rounds up to 8 pages = 32768
|
||||||
|
size_t expectedSlotStride = ((slotPadToNBytes + pageSize - 1) / pageSize) * pageSize;
|
||||||
|
EXPECT_EQ(expectedSlotStride, 32768u)
|
||||||
|
<< "32256 should round up to 32768 (8 pages)";
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||||
|
<< "Slot stride should be rounded up to " << expectedSlotStride;
|
||||||
|
|
||||||
|
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: Slot stride already a multiple of page size - should remain unchanged
|
||||||
|
TEST_F(StagingBufferTest, SlotStrideAlreadyMultipleOfPageSize) {
|
||||||
|
size_t nSlots = 30;
|
||||||
|
size_t slotPadToNBytes = pageSize * 3; // Already a multiple (e.g., 12288)
|
||||||
|
auto constraints = createTestConstraints(
|
||||||
|
pageSize, // slotStartAlignment
|
||||||
|
slotPadToNBytes, // slotPadToNBytes (already multiple)
|
||||||
|
pageSize, // frameStartAlignment
|
||||||
|
pageSize); // framePadToNBytes
|
||||||
|
|
||||||
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
|
|
||||||
|
// Should remain unchanged
|
||||||
|
size_t expectedSlotStride = slotPadToNBytes;
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||||
|
<< "Slot stride already a multiple of page size should remain unchanged";
|
||||||
|
|
||||||
|
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 6: Multiple different slot stride sizes in sequence
|
||||||
|
TEST_F(StagingBufferTest, MultipleDifferentSlotStrideSizes) {
|
||||||
|
// Test with various slot pad sizes
|
||||||
|
struct TestCase {
|
||||||
|
size_t slotPadToNBytes;
|
||||||
|
size_t expectedRoundedStride;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TestCase> testCases = {
|
||||||
|
{256, pageSize}, // Small: rounds to 1 page
|
||||||
|
{pageSize, pageSize}, // Equal: stays 1 page
|
||||||
|
{pageSize + 1, pageSize * 2}, // Slightly larger: rounds to 2 pages
|
||||||
|
{32256, 32768}, // Much larger: rounds to 8 pages
|
||||||
|
{pageSize * 5, pageSize * 5}, // Already multiple: stays 5 pages
|
||||||
|
{pageSize * 10 + 100, pageSize * 11}, // Large with remainder: rounds to 11 pages
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& testCase : testCases) {
|
||||||
|
size_t nSlots = 10;
|
||||||
|
auto constraints = createTestConstraints(
|
||||||
|
pageSize,
|
||||||
|
testCase.slotPadToNBytes,
|
||||||
|
pageSize,
|
||||||
|
pageSize);
|
||||||
|
|
||||||
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
|
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, testCase.expectedRoundedStride)
|
||||||
|
<< "Slot pad " << testCase.slotPadToNBytes
|
||||||
|
<< " should round to " << testCase.expectedRoundedStride;
|
||||||
|
|
||||||
|
verifyAllSlotsPageAligned(buffer, testCase.expectedRoundedStride, pageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 4: OpenCL Intensity constraints
|
// Test 7: Real-world scenario - PcloudIntensityStimulusBuffer (32256 bytes)
|
||||||
TEST_F(StagingBufferTest, OpenClIntensityConstraints) {
|
TEST_F(StagingBufferTest, RealWorldPcloudIntensityScenario) {
|
||||||
size_t nSlots = 909; // 30000ms / 33ms
|
size_t nSlots = 909; // histbuffMs=30000 / CONFIG_STIMBUFF_FRAME_PERIOD_MS=33
|
||||||
size_t nDgramsPerFrame = 84;
|
size_t nDgramsPerFrame = 84;
|
||||||
size_t nPointsPerDgram = 96;
|
size_t nPointsPerDgram = 96;
|
||||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 1; // 32256
|
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 1; // 32256
|
||||||
|
|
||||||
auto constraints = createTestConstraints(
|
auto constraints = createTestConstraints(
|
||||||
sizeof(float), // slotStartAlignment
|
pageSize, // slotStartAlignment
|
||||||
slotPadToNBytes, // slotPadToNBytes
|
slotPadToNBytes, // slotPadToNBytes
|
||||||
pageSize, // frameStartAlignment
|
pageSize, // frameStartAlignment
|
||||||
pageSize); // framePadToNBytes
|
pageSize); // framePadToNBytes
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
// Should round up to 32768 (8 pages)
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
size_t expectedSlotStride = 32768;
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||||
|
<< "Real-world 32256-byte slots should round to 32768";
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
|
||||||
<< "Buffer size should be page-aligned";
|
|
||||||
|
|
||||||
|
// Verify FrameAssemblyDesc
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
ASSERT_NE(frameDesc, nullptr);
|
||||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 5: OpenCL Ambience constraints
|
// Test 8: Real-world scenario - PcloudAmbienceStimulusBuffer (336 bytes)
|
||||||
TEST_F(StagingBufferTest, OpenClAmbienceConstraints) {
|
TEST_F(StagingBufferTest, RealWorldPcloudAmbienceScenario) {
|
||||||
size_t nSlots = 909; // 30000ms / 33ms
|
size_t nSlots = 909;
|
||||||
size_t nDgramsPerFrame = 84;
|
size_t nDgramsPerFrame = 84;
|
||||||
size_t slotPadToNBytes = nDgramsPerFrame * sizeof(float); // 336
|
size_t slotPadToNBytes = nDgramsPerFrame * sizeof(float); // 336
|
||||||
|
|
||||||
auto constraints = createTestConstraints(
|
|
||||||
sizeof(float), // slotStartAlignment
|
|
||||||
slotPadToNBytes, // slotPadToNBytes
|
|
||||||
pageSize, // frameStartAlignment
|
|
||||||
pageSize); // framePadToNBytes
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
|
||||||
<< "Buffer size should be page-aligned";
|
|
||||||
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
|
||||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 6: io_uring constraints
|
|
||||||
TEST_F(StagingBufferTest, IoUringConstraints) {
|
|
||||||
size_t nSlots = 84;
|
|
||||||
auto constraints = createTestConstraints(
|
auto constraints = createTestConstraints(
|
||||||
pageSize, // slotStartAlignment
|
pageSize, // slotStartAlignment
|
||||||
1472, // slotPadToNBytes
|
|
||||||
pageSize, // frameStartAlignment
|
|
||||||
pageSize); // framePadToNBytes
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
|
||||||
EXPECT_EQ(expectedSlotStride, pageSize) << "Slot stride should be max(alignment, padding) = pageSize";
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
|
||||||
<< "Buffer size should be page-aligned";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 7: Slot alignment larger than padding
|
|
||||||
TEST_F(StagingBufferTest, SlotAlignmentLargerThanPadding) {
|
|
||||||
size_t nSlots = 10;
|
|
||||||
auto constraints = createTestConstraints(64, 16, 64, 64);
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
|
||||||
EXPECT_EQ(expectedSlotStride, 64u) << "Slot stride should be max(64, 16) = 64";
|
|
||||||
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
if (!frameDesc->slots.empty()) {
|
|
||||||
verifyAlignment(frameDesc->slots[0].vaddr, 64, "First slot");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 8: Frame alignment larger than slot alignment
|
|
||||||
TEST_F(StagingBufferTest, FrameAlignmentLargerThanSlotAlignment) {
|
|
||||||
size_t nSlots = 10;
|
|
||||||
auto constraints = createTestConstraints(4, 16, pageSize, pageSize);
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % pageSize, 0u)
|
|
||||||
<< "Buffer size should be page-aligned";
|
|
||||||
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
if (!frameDesc->slots.empty()) {
|
|
||||||
verifyAlignment(frameDesc->slots[0].vaddr, pageSize, "First slot frame alignment");
|
|
||||||
verifyAlignment(frameDesc->slots[0].vaddr, 4, "First slot slot alignment");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 9: Slot alignment larger than frame alignment
|
|
||||||
TEST_F(StagingBufferTest, SlotAlignmentLargerThanFrameAlignment) {
|
|
||||||
size_t nSlots = 10;
|
|
||||||
size_t largeAlignment = 8192;
|
|
||||||
auto constraints = createTestConstraints(largeAlignment, 16, pageSize, pageSize);
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len % largeAlignment, 0u)
|
|
||||||
<< "Buffer size should be aligned to max alignment";
|
|
||||||
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
if (!frameDesc->slots.empty()) {
|
|
||||||
verifyAlignment(frameDesc->slots[0].vaddr, largeAlignment, "First slot");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 10: Minimum buffer size larger than calculated size
|
|
||||||
TEST_F(StagingBufferTest, MinimumBufferSizeEnforcement) {
|
|
||||||
size_t nSlots = 1;
|
|
||||||
auto constraints = createTestConstraints(4, 16, 4, 1024);
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
size_t actualBufferSize = buffer.getIoUringRegisterIoVec().iov_len;
|
|
||||||
|
|
||||||
EXPECT_GE(actualBufferSize, 1024u) << "Buffer size should be at least framePadToNBytes";
|
|
||||||
EXPECT_EQ(actualBufferSize, expectedBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 11: Actual PcloudIntensityStimulusBuffer scenario
|
|
||||||
TEST_F(StagingBufferTest, RealWorldPcloudIntensityScenario) {
|
|
||||||
size_t nSlots = 909; // histbuffMs=30000 / CONFIG_STIMBUFF_FRAME_PERIOD_MS=33
|
|
||||||
size_t nDgramsPerFrame = 84;
|
|
||||||
size_t nPointsPerDgram = 96; // SingleFirst/Strongest/Dual
|
|
||||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 1; // 32256
|
|
||||||
|
|
||||||
auto constraints = createTestConstraints(
|
|
||||||
sizeof(float), // slotStartAlignment
|
|
||||||
slotPadToNBytes, // slotPadToNBytes
|
slotPadToNBytes, // slotPadToNBytes
|
||||||
pageSize, // frameStartAlignment
|
pageSize, // frameStartAlignment
|
||||||
pageSize); // framePadToNBytes
|
pageSize); // framePadToNBytes
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
StagingBuffer buffer(constraints, constraints, nSlots);
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
// Should round up to page size
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
size_t expectedSlotStride = pageSize;
|
||||||
|
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride)
|
||||||
|
<< "Real-world 336-byte slots should round to page size";
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
verifyAllSlotsPageAligned(buffer, expectedSlotStride, pageSize);
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
|
|
||||||
// Verify the buffer can hold all slots
|
// Verify FrameAssemblyDesc
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
ASSERT_NE(frameDesc, nullptr);
|
||||||
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
EXPECT_EQ(frameDesc->numSlots, nSlots);
|
||||||
|
|
||||||
// Verify all slots fit within buffer
|
|
||||||
if (!frameDesc->slots.empty()) {
|
|
||||||
uint8_t* bufferStart = buffer.getIoUringRegisterIoVec().iov_base;
|
|
||||||
size_t bufferSize = buffer.getIoUringRegisterIoVec().iov_len;
|
|
||||||
uint8_t* lastSlotEnd = frameDesc->slots.back().vaddr + frameDesc->slots.back().nBytes;
|
|
||||||
EXPECT_LE(lastSlotEnd - bufferStart, static_cast<ptrdiff_t>(bufferSize))
|
|
||||||
<< "Last slot should fit within buffer";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 12: Different return modes - Triple (90 points)
|
|
||||||
TEST_F(StagingBufferTest, TripleReturnModeMesh) {
|
|
||||||
size_t nSlots = 909;
|
|
||||||
size_t nDgramsPerFrame = 84;
|
|
||||||
size_t nPointsPerDgram = 90; // Triple return mode
|
|
||||||
size_t slotPadToNBytes = nDgramsPerFrame * nPointsPerDgram * sizeof(float) * 3; // 90720
|
|
||||||
|
|
||||||
auto constraints = createTestConstraints(
|
|
||||||
sizeof(float), // slotStartAlignment
|
|
||||||
slotPadToNBytes, // slotPadToNBytes
|
|
||||||
pageSize, // frameStartAlignment
|
|
||||||
pageSize); // framePadToNBytes
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
size_t expectedSlotStride = calculateExpectedSlotStride(constraints);
|
|
||||||
size_t expectedBufferSize = calculateExpectedBufferSize(nSlots, constraints);
|
|
||||||
|
|
||||||
EXPECT_EQ(buffer.slotStrideNBytes, expectedSlotStride);
|
|
||||||
EXPECT_EQ(buffer.getIoUringRegisterIoVec().iov_len, expectedBufferSize);
|
|
||||||
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
EXPECT_EQ(frameDesc->slotSizeBytes, slotPadToNBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test 13: Verify firstSlotOffsetNBytes is valid
|
|
||||||
TEST_F(StagingBufferTest, FirstSlotOffsetValidation) {
|
|
||||||
size_t nSlots = 100;
|
|
||||||
auto constraints = createTestConstraints(4, 16, pageSize, pageSize);
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
EXPECT_LE(buffer.firstSlotOffsetNBytes, buffer.getIoUringRegisterIoVec().iov_len)
|
|
||||||
<< "First slot offset should be within buffer";
|
|
||||||
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
if (!frameDesc->slots.empty()) {
|
|
||||||
uint8_t* bufferStart = buffer.getIoUringRegisterIoVec().iov_base;
|
|
||||||
uint8_t* firstSlot = frameDesc->slots[0].vaddr;
|
|
||||||
size_t calculatedOffset = firstSlot - bufferStart;
|
|
||||||
EXPECT_EQ(calculatedOffset, buffer.firstSlotOffsetNBytes)
|
|
||||||
<< "First slot offset should match calculated value";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 14: Verify all slots are properly aligned
|
|
||||||
TEST_F(StagingBufferTest, AllSlotsProperlyAligned) {
|
|
||||||
size_t nSlots = 50;
|
|
||||||
auto constraints = createTestConstraints(64, 64, 64, 64);
|
|
||||||
|
|
||||||
StagingBuffer buffer(constraints, constraints, nSlots);
|
|
||||||
|
|
||||||
auto frameDesc = static_cast<std::shared_ptr<FrameAssemblyDesc>>(buffer);
|
|
||||||
ASSERT_NE(frameDesc, nullptr);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < frameDesc->slots.size(); ++i) {
|
|
||||||
verifyAlignment(frameDesc->slots[i].vaddr, constraints.slotStartAlignmentByteVal,
|
|
||||||
"Slot " + std::to_string(i));
|
|
||||||
verifyAlignment(frameDesc->slots[i].vaddr, constraints.frameStartAlignmentByteVal,
|
|
||||||
"Slot " + std::to_string(i) + " frame alignment");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace stim_buff
|
} // namespace stim_buff
|
||||||
} // namespace smo
|
} // namespace smo
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user