mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-05 00:58:05 +02:00
util: Add range_minimum_query
Reviewed-by: Alyssa Rosenzweig <alyssa.rosenzweig@intel.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36184>
This commit is contained in:
parent
cd06366ca2
commit
0d07b86073
4 changed files with 194 additions and 0 deletions
|
|
@ -99,6 +99,8 @@ files_mesa_util = files(
|
||||||
'ralloc.h',
|
'ralloc.h',
|
||||||
'rand_xor.c',
|
'rand_xor.c',
|
||||||
'rand_xor.h',
|
'rand_xor.h',
|
||||||
|
'range_minimum_query.c',
|
||||||
|
'range_minimum_query.h',
|
||||||
'rb_tree.c',
|
'rb_tree.c',
|
||||||
'rb_tree.h',
|
'rb_tree.h',
|
||||||
'register_allocate.c',
|
'register_allocate.c',
|
||||||
|
|
@ -400,6 +402,7 @@ if with_tests
|
||||||
'tests/mesa-sha1_test.cpp',
|
'tests/mesa-sha1_test.cpp',
|
||||||
'tests/os_mman_test.cpp',
|
'tests/os_mman_test.cpp',
|
||||||
'tests/perf/u_trace_test.cpp',
|
'tests/perf/u_trace_test.cpp',
|
||||||
|
'tests/range_minimum_query_test.cpp',
|
||||||
'tests/rb_tree_test.cpp',
|
'tests/rb_tree_test.cpp',
|
||||||
'tests/register_allocate_test.cpp',
|
'tests/register_allocate_test.cpp',
|
||||||
'tests/roundeven_test.cpp',
|
'tests/roundeven_test.cpp',
|
||||||
|
|
|
||||||
72
src/util/range_minimum_query.c
Normal file
72
src/util/range_minimum_query.c
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 Valve Corporation
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "range_minimum_query.h"
|
||||||
|
|
||||||
|
#include "bitscan.h"
|
||||||
|
#include "macros.h"
|
||||||
|
#include "ralloc.h"
|
||||||
|
#include "u_math.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
range_minimum_query_table_resize(struct range_minimum_query_table *table,
|
||||||
|
void *mem_ctx, uint32_t width)
|
||||||
|
{
|
||||||
|
const uint64_t height = util_logbase2_64(width) + 1;
|
||||||
|
const uint64_t size = width * height;
|
||||||
|
assert(size < UINT32_MAX);
|
||||||
|
|
||||||
|
table->table = reralloc_array_size(mem_ctx, table->table,
|
||||||
|
sizeof(uint32_t), size);
|
||||||
|
table->width = width;
|
||||||
|
table->height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
elementwise_minimum(uint32_t *restrict out,
|
||||||
|
uint32_t *const a,
|
||||||
|
uint32_t *const b,
|
||||||
|
uint32_t count)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
|
out[i] = MIN2(a[i], b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
rmq_distance(int32_t level)
|
||||||
|
{
|
||||||
|
return 1 << level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
range_minimum_query_table_preprocess(struct range_minimum_query_table *table)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 1; i < table->height; i++) {
|
||||||
|
uint32_t in_distance = rmq_distance(i - 1);
|
||||||
|
uint32_t *in_row = table->table + table->width * (i - 1);
|
||||||
|
uint32_t *out_row = table->table + table->width * i;
|
||||||
|
elementwise_minimum(out_row, in_row, in_row + in_distance,
|
||||||
|
table->width - in_distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
range_minimum_query(struct range_minimum_query_table *const table,
|
||||||
|
uint32_t left_idx, uint32_t right_idx)
|
||||||
|
{
|
||||||
|
assert(left_idx < right_idx);
|
||||||
|
const uint32_t distance = right_idx - left_idx;
|
||||||
|
|
||||||
|
uint32_t level = util_logbase2(distance);
|
||||||
|
assert(rmq_distance(level) <= distance);
|
||||||
|
assert(distance < 2 * rmq_distance(level));
|
||||||
|
assert(level < table->height);
|
||||||
|
|
||||||
|
uint32_t *const row = table->table + table->width * level;
|
||||||
|
uint32_t left = row[left_idx];
|
||||||
|
uint32_t right = row[right_idx - rmq_distance(level)];
|
||||||
|
return MIN2(left, right);
|
||||||
|
}
|
||||||
65
src/util/range_minimum_query.h
Normal file
65
src/util/range_minimum_query.h
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 Valve Corporation
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RANGE_MINIMUM_QUERY_H
|
||||||
|
#define RANGE_MINIMUM_QUERY_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the smallest integer in a portion of an array.
|
||||||
|
*
|
||||||
|
* We use the simple RMQ algorithm that uses O(n log n) preprcessing time and
|
||||||
|
* O(1) query time (see eg. Bender and Colton section 3).
|
||||||
|
*
|
||||||
|
* Bender, M.A., Farach-Colton, M. (2000). The LCA Problem Revisited. In:
|
||||||
|
* Gonnet, G.H., Viola, A. (eds) LATIN 2000: Theoretical Informatics. LATIN
|
||||||
|
* 2000. Lecture Notes in Computer Science, vol 1776. Springer, Berlin,
|
||||||
|
* Heidelberg. https://doi.org/10.1007/10719839_9
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct range_minimum_query_table {
|
||||||
|
uint32_t *table;
|
||||||
|
uint32_t width, height;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
range_minimum_query_table_init(struct range_minimum_query_table *table)
|
||||||
|
{
|
||||||
|
memset((void*) table, 0, sizeof(*table));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
range_minimum_query_table_resize(struct range_minimum_query_table *table,
|
||||||
|
void *mem_ctx, uint32_t width);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform preprocessing on the table to ready it for queries.
|
||||||
|
*
|
||||||
|
* Takes O(n log n) time.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
range_minimum_query_table_preprocess(struct range_minimum_query_table *table);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the smallest value in the array among indices in the half-open interval
|
||||||
|
* [left_idx, right_idx).
|
||||||
|
*
|
||||||
|
* Takes O(1) time.
|
||||||
|
*/
|
||||||
|
uint32_t
|
||||||
|
range_minimum_query(struct range_minimum_query_table *const table,
|
||||||
|
uint32_t left_idx, uint32_t right_idx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
54
src/util/tests/range_minimum_query_test.cpp
Normal file
54
src/util/tests/range_minimum_query_test.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 Valve Corporation
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "ralloc.h"
|
||||||
|
#include "range_minimum_query.h"
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
rmq_naive(struct range_minimum_query_table *const table,
|
||||||
|
uint32_t left_idx, uint32_t right_idx)
|
||||||
|
{
|
||||||
|
uint32_t result = UINT32_MAX;
|
||||||
|
for (uint32_t i = left_idx; i < right_idx; i++) {
|
||||||
|
result = MIN2(result, table->table[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(range_minimum_query_test, range_minimum_query_test)
|
||||||
|
{
|
||||||
|
void* context = ralloc_context(nullptr);
|
||||||
|
|
||||||
|
std::mt19937 gen(1337);
|
||||||
|
|
||||||
|
struct range_minimum_query_table table;
|
||||||
|
range_minimum_query_table_init(&table);
|
||||||
|
|
||||||
|
for (uint32_t width = 0; width < 256; width++) {
|
||||||
|
range_minimum_query_table_resize(&table, context, width);
|
||||||
|
|
||||||
|
std::uniform_int_distribution<> distrib(0, 100);
|
||||||
|
for (uint32_t i = 0; i < width; i++) {
|
||||||
|
table.table[i] = distrib(gen);
|
||||||
|
// printf("%i\n", table.table[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
range_minimum_query_table_preprocess(&table);
|
||||||
|
for (uint32_t i = 0; i < width; i++) {
|
||||||
|
for (uint32_t j = i + 1; j < width; j++) {
|
||||||
|
// printf("%i, %i\n", i, j);
|
||||||
|
EXPECT_EQ(range_minimum_query(&table, i, j),
|
||||||
|
rmq_naive(&table, i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ralloc_free(context);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue