blob: a3abf82b863fc5872b204a60c493a6961ff3f4e4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <gtest/gtest.h>
#include "../src/runtime/hexagon/ring_buffer.h"
using namespace tvm::runtime;
using namespace tvm::runtime::hexagon;
class RingBufferTest : public ::testing::Test {
void SetUp() override { ring_buff = new RingBuffer<int>(size, in_flight); }
void TearDown() override { delete ring_buff; }
public:
std::function<bool(int*)> in_flight = [](int* ptr) {
if (*ptr == 42) {
// finished
return false;
}
// in flight
return true;
};
int finished = 42;
int inflight = 43;
uint32_t size = 8;
uint32_t half = size / 2;
RingBuffer<int>* ring_buff = nullptr;
};
TEST_F(RingBufferTest, zero_size_ring_buffer) {
ASSERT_THROW(RingBuffer<int>(0, in_flight), InternalError);
}
TEST_F(RingBufferTest, in_flight) { ASSERT_EQ(ring_buff->InFlight(), 0); }
TEST_F(RingBufferTest, next) {
// get pointer to first item
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
// mark it in flight and check
*ptr = inflight;
ASSERT_EQ(ring_buff->InFlight(), 1);
// mark it finished and check
*ptr = finished;
ASSERT_EQ(ring_buff->InFlight(), 0);
}
TEST_F(RingBufferTest, full) {
// fill the ring buffer
for (int i = 0; i < size; ++i) {
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
// mark in flight and check
*ptr = inflight;
ASSERT_EQ(ring_buff->InFlight(), i + 1);
}
// check that the ring buffer is full
ASSERT_EQ(ring_buff->Next(), nullptr);
ASSERT_EQ(ring_buff->InFlight(), size);
}
TEST_F(RingBufferTest, wrap) {
// fill the ring buffer, but mark each finished
bool first = true;
int* firstptr = nullptr;
for (int i = 0; i < size; ++i) {
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
// save first ptr for later comparison
if (first) {
firstptr = ptr;
first = false;
}
// mark finished and check
*ptr = finished;
ASSERT_EQ(ring_buff->InFlight(), 0);
}
// reuse the first ring buffer entry
int* ptr = ring_buff->Next();
ASSERT_EQ(ptr, firstptr);
// mark it in flight and check
*ptr = inflight;
ASSERT_EQ(ring_buff->InFlight(), 1);
// mark it finished and check
*ptr = finished;
ASSERT_EQ(ring_buff->InFlight(), 0);
}
TEST_F(RingBufferTest, wrap_corner) {
for (int i = 0; i < size; ++i) {
int* ptr = ring_buff->Next();
*ptr = finished;
}
// reuse the first ring buffer entry
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
// user must mark the item "inflight" before checking in flight count
// here the "finished" status is inherited from the reused ring buffer entry
// thus the in flight count is zero instead one; which the user might expect
ASSERT_EQ(ring_buff->InFlight(), 0);
// marking the item "inflight" after checking the in flight count
// will not change the outcome; the ring buffer considers the item "finished"
*ptr = inflight;
ASSERT_EQ(ring_buff->InFlight(), 0);
}
TEST_F(RingBufferTest, half_in_flight) {
// these will complete
for (int i = 0; i < half; ++i) {
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
*ptr = finished;
ASSERT_EQ(ring_buff->InFlight(), 0);
}
// these will not complete
for (int i = 0; i < half; ++i) {
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
*ptr = inflight;
ASSERT_EQ(ring_buff->InFlight(), i + 1);
}
// check half in flight
ASSERT_EQ(ring_buff->InFlight(), half);
// get pointer to next item
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
// mark it inflight and check
*ptr = inflight;
ASSERT_EQ(ring_buff->InFlight(), half + 1);
// mark it finished and check also blocked
*ptr = finished;
ASSERT_EQ(ring_buff->InFlight(), half + 1);
}
TEST_F(RingBufferTest, half_in_flight_blocked) {
// these will not complete
for (int i = 0; i < half; ++i) {
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
*ptr = inflight;
ASSERT_EQ(ring_buff->InFlight(), i + 1);
}
// these would complete, but they are blocked
for (int i = half; i < size; ++i) {
int* ptr = ring_buff->Next();
ASSERT_NE(ptr, nullptr);
*ptr = finished;
ASSERT_EQ(ring_buff->InFlight(), i + 1);
}
// check that the ring buffer is full
ASSERT_EQ(ring_buff->Next(), nullptr);
ASSERT_EQ(ring_buff->InFlight(), size);
}
class QueuedRingBufferTest : public RingBufferTest {
void SetUp() override {
queued_ring_buff = new QueuedRingBuffer<int>(MAX_QUEUES, size, in_flight);
}
void TearDown() override { delete queued_ring_buff; }
public:
int MAX_QUEUES = 2;
QueuedRingBuffer<int>* queued_ring_buff = nullptr;
};
TEST_F(QueuedRingBufferTest, invalid_queue) {
ASSERT_THROW(queued_ring_buff->Next(MAX_QUEUES), InternalError);
ASSERT_THROW(queued_ring_buff->InFlight(MAX_QUEUES), InternalError);
ASSERT_THROW(queued_ring_buff->StartGroup(MAX_QUEUES), InternalError);
ASSERT_THROW(queued_ring_buff->EndGroup(MAX_QUEUES), InternalError);
}
TEST_F(QueuedRingBufferTest, two_queues) {
int* q0 = queued_ring_buff->Next(0);
*q0 = inflight;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
ASSERT_EQ(queued_ring_buff->InFlight(1), 0);
int* q1 = queued_ring_buff->Next(1);
*q1 = inflight;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
ASSERT_EQ(queued_ring_buff->InFlight(1), 1);
*q0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
ASSERT_EQ(queued_ring_buff->InFlight(1), 1);
*q1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
ASSERT_EQ(queued_ring_buff->InFlight(1), 0);
}
TEST_F(QueuedRingBufferTest, group_end_before_group_start) {
ASSERT_THROW(queued_ring_buff->EndGroup(0), InternalError);
}
TEST_F(QueuedRingBufferTest, group_restart) {
queued_ring_buff->StartGroup(0);
ASSERT_THROW(queued_ring_buff->StartGroup(0), InternalError);
}
TEST_F(QueuedRingBufferTest, zero_size_group) {
queued_ring_buff->StartGroup(0);
ASSERT_THROW(queued_ring_buff->EndGroup(0), InternalError);
}
TEST_F(QueuedRingBufferTest, in_flight_before_group_end) {
queued_ring_buff->StartGroup(0);
ASSERT_THROW(queued_ring_buff->InFlight(0), InternalError);
}
TEST_F(QueuedRingBufferTest, group_of_one) {
queued_ring_buff->StartGroup(0);
int* g0_0 = queued_ring_buff->Next(0);
*g0_0 = inflight;
queued_ring_buff->EndGroup(0);
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
*g0_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
}
TEST_F(QueuedRingBufferTest, group_of_two) {
queued_ring_buff->StartGroup(0);
int* g0_0 = queued_ring_buff->Next(0);
*g0_0 = inflight;
int* g0_1 = queued_ring_buff->Next(0);
*g0_1 = inflight;
queued_ring_buff->EndGroup(0);
// neither done => group in flight
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
// half done => group in flight
*g0_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
// both done => group finished
*g0_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
}
TEST_F(QueuedRingBufferTest, group_of_three) {
queued_ring_buff->StartGroup(0);
int* g0_0 = queued_ring_buff->Next(0);
*g0_0 = inflight;
int* g0_1 = queued_ring_buff->Next(0);
*g0_1 = inflight;
int* g0_2 = queued_ring_buff->Next(0);
*g0_2 = inflight;
queued_ring_buff->EndGroup(0);
// neither done => group in flight
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
// 1/3 done => group in flight
*g0_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
// 2/3 done => group in flight
*g0_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
// all done => group finished
*g0_2 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
}
TEST_F(QueuedRingBufferTest, two_groups_of_two) {
queued_ring_buff->StartGroup(0);
int* g0_0 = queued_ring_buff->Next(0);
*g0_0 = inflight;
int* g0_1 = queued_ring_buff->Next(0);
*g0_1 = inflight;
queued_ring_buff->EndGroup(0);
queued_ring_buff->StartGroup(0);
int* g1_0 = queued_ring_buff->Next(0);
*g1_0 = inflight;
int* g1_1 = queued_ring_buff->Next(0);
*g1_1 = inflight;
queued_ring_buff->EndGroup(0);
// two groups in flight
ASSERT_EQ(queued_ring_buff->InFlight(0), 2);
// group 0 half done => two groups in flight
*g0_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 2);
// group 0 done => one group in flight
*g0_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
// group 1 half done => one group in flight
*g1_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
// group 1 done => zero groups in flight
*g1_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
}
TEST_F(QueuedRingBufferTest, two_queues_two_groups_of_two) {
queued_ring_buff->StartGroup(0);
int* q0g0_0 = queued_ring_buff->Next(0);
*q0g0_0 = inflight;
int* q0g0_1 = queued_ring_buff->Next(0);
*q0g0_1 = inflight;
queued_ring_buff->EndGroup(0);
queued_ring_buff->StartGroup(1);
int* q1g0_0 = queued_ring_buff->Next(1);
*q1g0_0 = inflight;
int* q1g0_1 = queued_ring_buff->Next(1);
*q1g0_1 = inflight;
queued_ring_buff->EndGroup(1);
queued_ring_buff->StartGroup(0);
int* q0g1_0 = queued_ring_buff->Next(0);
*q0g1_0 = inflight;
int* q0g1_1 = queued_ring_buff->Next(0);
*q0g1_1 = inflight;
queued_ring_buff->EndGroup(0);
queued_ring_buff->StartGroup(1);
int* q1g1_0 = queued_ring_buff->Next(1);
*q1g1_0 = inflight;
int* q1g1_1 = queued_ring_buff->Next(1);
*q1g1_1 = inflight;
queued_ring_buff->EndGroup(1);
// two queues with two groups in flight each
ASSERT_EQ(queued_ring_buff->InFlight(0), 2);
ASSERT_EQ(queued_ring_buff->InFlight(1), 2);
// queue 0 group 0 half done => no change
*q0g0_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 2);
ASSERT_EQ(queued_ring_buff->InFlight(1), 2);
// queue 0 group 0 done => queue 0 with one group in flight
*q0g0_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
ASSERT_EQ(queued_ring_buff->InFlight(1), 2);
// queue 1 group 0 half done => no change
*q1g0_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
ASSERT_EQ(queued_ring_buff->InFlight(1), 2);
// queue 1 group 0 done => queue 1 with one group in flight
*q1g0_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
ASSERT_EQ(queued_ring_buff->InFlight(1), 1);
// queue 0 group 1 half done => no change
*q0g1_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 1);
ASSERT_EQ(queued_ring_buff->InFlight(1), 1);
// queue 0 group 1 done => queue 0 with zero groups in flight
*q0g1_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
ASSERT_EQ(queued_ring_buff->InFlight(1), 1);
// queue 1 group 1 half done => no change
*q1g1_0 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
ASSERT_EQ(queued_ring_buff->InFlight(1), 1);
// queue 1 group 1 done => queue 1 with zero groups in flight
*q1g1_1 = finished;
ASSERT_EQ(queued_ring_buff->InFlight(0), 0);
ASSERT_EQ(queued_ring_buff->InFlight(1), 0);
}