blob: af1c58b1e2e031beb303b31c3c867bbd41e590fb [file] [edit]
// Copyright 2024 The clvk authors.
//
// Licensed 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 "queue_controller.hpp"
#include "queue.hpp"
cvk_queue_controller_batch_parameters::cvk_queue_controller_batch_parameters(
cvk_command_queue* queue)
: cvk_queue_controller(queue),
m_max_cmd_batch_size_limit(queue->device()->get_max_cmd_batch_size()),
m_max_first_cmd_batch_size_limit(
queue->device()->get_max_first_cmd_batch_size()),
m_max_first_cmd_batch_size_limit_hit(0), m_last_batch_size(0),
m_no_batch_in_flight_since_last_flush(false),
m_executor_idle_since_last_flush(false) {
TRACE_CNT_VAR_INIT(max_cmd_batch_size_counter,
"clvk-queue_" + std::to_string((uintptr_t)queue) +
"-max_batch_size",
queue->track());
TRACE_CNT_VAR_INIT(max_first_cmd_batch_size_counter,
"clvk-queue_" + std::to_string((uintptr_t)queue) +
"-max_first_batch_size",
queue->track());
TRACE_CNT_VAR_INIT(max_first_cmd_batch_size_limit_counter,
"clvk-queue_" + std::to_string((uintptr_t)queue) +
"-max_first_batch_size_limit",
queue->track());
TRACE_CNT_VAR_INIT(max_first_cmd_batch_size_limit_hit_counter,
"clvk-queue_" + std::to_string((uintptr_t)queue) +
"-max_first_batch_size_limit_hit",
queue->track());
TRACE_CNT_VAR_INIT(last_batch_size_counter,
"clvk-queue_" + std::to_string((uintptr_t)queue) +
"-last_batch_size",
queue->track());
TRACE_CNT(max_cmd_batch_size_counter, queue->m_max_cmd_batch_size);
TRACE_CNT(max_first_cmd_batch_size_counter,
queue->m_max_first_cmd_batch_size);
TRACE_CNT(max_first_cmd_batch_size_limit_counter,
m_max_first_cmd_batch_size_limit);
TRACE_CNT(max_first_cmd_batch_size_limit_hit_counter, 0);
TRACE_CNT(last_batch_size_counter, m_last_batch_size);
}
void cvk_queue_controller_batch_parameters::update_trace_counter() {
TRACE_CNT(max_cmd_batch_size_counter, m_queue->m_max_cmd_batch_size);
TRACE_CNT(max_first_cmd_batch_size_counter,
m_queue->m_max_first_cmd_batch_size);
TRACE_CNT(max_first_cmd_batch_size_limit_counter,
m_max_first_cmd_batch_size_limit);
TRACE_CNT(max_first_cmd_batch_size_limit_hit_counter,
m_max_first_cmd_batch_size_limit_hit);
TRACE_CNT(last_batch_size_counter, m_last_batch_size);
}
void cvk_queue_controller_batch_parameters::reset_after_flush() {
if (m_queue->m_nb_batch_in_flight > 2 &&
!m_no_batch_in_flight_since_last_flush) {
// Increase max_cmd_batch_size if there was always batches in flight
// since last flush.
m_queue->m_max_cmd_batch_size += m_queue->m_nb_batch_in_flight;
}
// Reset after flush
m_last_batch_size = 0;
m_no_batch_in_flight_since_last_flush = false;
m_executor_idle_since_last_flush = false;
}
void cvk_queue_controller_batch_parameters::update_after_empty_flush() {
TRACE_FUNCTION();
reset_after_flush();
update_trace_counter();
}
void cvk_queue_controller_batch_parameters::
update_after_end_current_command_batch(bool from_flush) {
TRACE_FUNCTION();
if (!m_queue->m_command_batch) {
if (from_flush) {
reset_after_flush();
}
update_trace_counter();
return;
}
// update m_executor_idle_since_last_flush
if (m_queue->m_executor != nullptr) {
m_executor_idle_since_last_flush |= m_queue->m_executor->is_idle();
}
auto batch_size = m_queue->m_command_batch->batch_size();
if (m_last_batch_size == 0) {
m_last_batch_size = batch_size;
update_trace_counter();
return;
}
// update m_no_batch_in_flight_since_last_flush
// It needs to be after m_last_batch_size is first initialized as we do not
// care of what is happening before the first batch.
m_no_batch_in_flight_since_last_flush |= m_queue->m_nb_batch_in_flight == 0;
if (m_queue->m_nb_batch_in_flight == 0 &&
m_last_batch_size == m_queue->m_max_first_cmd_batch_size) {
// Nothing in flight and we flush because of first_cmd_batch_size
// reached. It should only happen the first time. Otherwise it means
// that our batches were too small to have the time to enqueue. Let's
// increase the first_cmd_batch_size_limit, reset the
// first_cmd_batch_size_limit_hit, increase first_cmd_batch_size to
// create bigger batch, and reset max_cmd_batch_size to make many small
// (size of first) batches to begin with.
m_max_first_cmd_batch_size_limit =
m_queue->m_max_first_cmd_batch_size + 1;
m_max_first_cmd_batch_size_limit_hit = 0;
m_queue->m_max_first_cmd_batch_size += 5;
m_queue->m_max_cmd_batch_size = m_queue->m_max_first_cmd_batch_size;
} else if (m_queue->m_nb_batch_in_flight == 0 &&
m_queue->m_max_cmd_batch_size >=
m_queue->m_max_first_cmd_batch_size + 2) {
// Nothing in flight and we flush because of either a flush or
// max_cmd_batch_size has been reached. Decrease max_cmd_batch_size if
// it does not go under max_first_cmd_batch_size to try to create batch
// before the end of the first batch.
m_queue->m_max_cmd_batch_size -= 2;
} else if (m_queue->m_nb_batch_in_flight > 0 &&
m_last_batch_size <= m_queue->m_max_first_cmd_batch_size &&
m_executor_idle_since_last_flush) {
// Commands in flight and the last batch was smaller that
// max_first_cmd_batch_size and the executor has been idle. Make smaller
// first batch to try to reduce the latency.
m_queue->m_max_first_cmd_batch_size -= 1;
}
if (from_flush) {
if (!m_executor_idle_since_last_flush &&
m_max_first_cmd_batch_size_limit_hit > 0) {
// Executor has always been busy.
// Decrease limit_hot to avoid first batch to decrease when we can
// keep it as big as possible.
m_max_first_cmd_batch_size_limit_hit--;
}
reset_after_flush();
} else {
m_last_batch_size = batch_size;
}
// Do not increate m_max_cmd_batch_size over the initial value
if (m_queue->m_max_cmd_batch_size > m_max_cmd_batch_size_limit) {
m_queue->m_max_cmd_batch_size = m_max_cmd_batch_size_limit;
}
// Do not decrease m_max_first_cmd_batch_size under the limit.
if (m_queue->m_max_first_cmd_batch_size <
m_max_first_cmd_batch_size_limit) {
m_max_first_cmd_batch_size_limit_hit += 2;
if (m_max_first_cmd_batch_size_limit_hit > 8 &&
m_max_first_cmd_batch_size_limit > 1) {
// Too many hit, reset limit hit and decrease limit
m_max_first_cmd_batch_size_limit_hit = 0;
m_max_first_cmd_batch_size_limit--;
} else if (m_max_first_cmd_batch_size_limit_hit > 16 &&
m_max_first_cmd_batch_size_limit == 1) {
// Too many hit with limit at minimum, we might have end up in a
// corner case, let's reset limit to initial value
m_max_first_cmd_batch_size_limit_hit = 0;
m_max_first_cmd_batch_size_limit =
m_queue->device()->get_max_first_cmd_batch_size();
} else {
m_queue->m_max_first_cmd_batch_size =
m_max_first_cmd_batch_size_limit;
}
}
// max_first_cmd_batch_size should not get bigger than max_cmd_batch_size.
if (m_queue->m_max_cmd_batch_size < m_queue->m_max_first_cmd_batch_size) {
m_queue->m_max_first_cmd_batch_size = m_queue->m_max_cmd_batch_size;
}
update_trace_counter();
}