Skip to content

Commit

Permalink
implement fast linear search for matching hashes
Browse files Browse the repository at this point in the history
I was surprised to see how this linear search can be vectorized by clang
since it doesn't have any early termination conditions. It might well be
more optimal than a binary search for small ranges simply because
there's no branch misprediction or cache thrashing.

To take advantage of this algorithm, we cut the search space using
binary search until it's <= 256 elements, and then do the rest as
a clang-vectorized linear search to avoid the heavy branch misprediction
cost for the last several iterations.

Signed-off-by: Steven Noonan <steven@uplinklabs.net>
  • Loading branch information
tycho committed Dec 24, 2023
1 parent c3e9432 commit b8913a2
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 36 deletions.
41 changes: 33 additions & 8 deletions glad/generator/c/templates/base_template.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*/
{% block includes %}
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
Expand Down Expand Up @@ -54,25 +55,49 @@ static uint64_t GLAD_{{ feature_set.name|api }}_ext_hashes[] = {
{% endfor %}
};

static int32_t glad_hash_search(const uint64_t *arr, uint32_t size, uint64_t target) {
int32_t left = 0;
int32_t right = (int32_t)size - 1;
static bool glad_hash_search_linear(const uint64_t *arr, uint32_t size, uint64_t target) {
/* Linear search. This may look awful, but Clang can vectorize this yielding
* a very high IPC. */
uint32_t i;
bool found = false;
for (i = 0; i < size; ++i) {
if (arr[i] == target)
found = true;
}
return found;
}

GLAD_NO_INLINE static bool glad_hash_search(const uint64_t *arr, uint32_t size, uint64_t target) {
/* Binary search approach. arr[] must be sorted for this to work. */
uint32_t left;
uint32_t right;

if (size < 1)
return false;

left = 0;
right = size - 1;

while (left <= right) {
int32_t mid = left + (right - left) / 2;
uint32_t rangesize = (right - left);
uint32_t mid = left + rangesize / 2;

if (arr[mid] == target) {
return mid;
return true;
}

if (rangesize <= 256) {
return glad_hash_search_linear(&arr[left], rangesize, target);
}

if (arr[mid] < target) {
left = mid + 1;
}
else {
} else {
right = mid - 1;
}
}

return -1;
return false;
}

static int glad_hash_compare(const void *_l, const void *_r)
Expand Down
7 changes: 1 addition & 6 deletions glad/generator/c/templates/egl.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,7 @@ static int glad_egl_get_extensions({{ template_utils.context_arg(', ') }}EGLDisp
}

static int glad_egl_has_extension(uint64_t *exts, uint32_t num_exts, uint64_t extension) {
int32_t match = glad_hash_search(exts, num_exts, extension);

if (match >= 0)
return 1;

return 0;
return glad_hash_search(exts, num_exts, extension);
}

static GLADapiproc glad_egl_get_proc_from_userptr(void *userptr, const char *name) {
Expand Down
8 changes: 1 addition & 7 deletions glad/generator/c/templates/gl.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,7 @@ static void glad_gl_free_extensions(uint64_t *exts) {
}

static int glad_gl_has_extension(uint64_t *exts, uint32_t num_exts, uint64_t ext) {

int32_t match = glad_hash_search(exts, num_exts, ext);

if (match >= 0)
return 1;

return 0;
return glad_hash_search(exts, num_exts, ext);
}

static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) {
Expand Down
9 changes: 1 addition & 8 deletions glad/generator/c/templates/loader/vulkan.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ static int glad_vulkan_is_device_function(const char *name) {
*
* `vkGetDeviceProcAddr` does not return NULL for non-device functions.
*/
const uint32_t length = GLAD_ARRAYSIZE(DEVICE_FUNCTIONS);
int32_t match;

match = glad_hash_search(DEVICE_FUNCTIONS, length, glad_hash_string(name, strlen(name)));
if (match >= 0)
return 1;

return 0;
return glad_hash_search(DEVICE_FUNCTIONS, GLAD_ARRAYSIZE(DEVICE_FUNCTIONS), glad_hash_string(name, strlen(name)));
}

struct _glad_vulkan_userptr {
Expand Down
8 changes: 1 addition & 7 deletions glad/generator/c/templates/vk.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,7 @@ static void glad_vk_free_extensions(uint64_t *extensions) {
}

static int glad_vk_has_extension(uint64_t name, uint32_t extension_count, uint64_t *extensions) {
int match;

match = glad_hash_search(extensions, extension_count, name);
if (match >= 0)
return 1;

return 0;
return glad_hash_search(extensions, extension_count, name);
}

static GLADapiproc glad_vk_get_proc_from_userptr(void *userptr, const char* name) {
Expand Down

0 comments on commit b8913a2

Please sign in to comment.