Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

boost::histogram::axis::variant, allow users to choose between sorted_array+std::lower_bound and eytzinger_layout+eytzinger_binary_search

Open
#369 14 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
30/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
cpp
Domain
performance

Research direction

Start by locating boost::histogram::axis::variant and the test referenced in the issue, then review how it currently uses std::upper_bound. Compare the two proposed search layouts and determine how users would choose between them; done means the choice is exposed without changing existing behavior and the relevant tests or benchmarks cover both options.

Written by the indexing model from the issue text.

Description

According to the test,

(boost::histogram::axis::variant is currently using std::upper_bound.)

comparison (x86-64 gcc (trunk), -std=c++23 -O3 -Wall -pedantic -pthread):

#pragma GCC optimize("O3")
#include <bits/stdc++.h>

constexpr size_t n = (1<<20);
constexpr size_t block_size = 64 / sizeof(int); // = 64 / 4 = cache_line_size / sizeof(int)
alignas(64) int a[n], b[n+1];

constexpr size_t query_count=(1<<20);
int targets[query_count];
int results_eytzinger[query_count];
int results_lower_bound[query_count];

int eytzinger(int i = 0, size_t k = 1) {
    if (k <= n) {
        i = eytzinger(i, 2 * k);
        b[k] = a[i++];
        i = eytzinger(i, 2 * k + 1);
    }
    return i;
}

int search(int x) {
    size_t k = 1;
    while (k <= n) {
        __builtin_prefetch(b + k * block_size);
        k = 2 * k + (b[k] < x);
    }
    k >>= __builtin_ffs(~k);
    return k;
}

int main()
{
    std::random_device rd;  //Will be used to obtain a seed for the random number engine
    std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    std::uniform_int_distribution<> distrib(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
    for (size_t i = 0; i != n; ++i)
    {
        a[i]=std::round(distrib(gen));
    }
    std::sort(std::begin(a),std::end(a));

    eytzinger();

    for (size_t i = 0; i != query_count; ++i)
    {
        targets[i]=std::round(distrib(gen));
    }

    {
        auto start = std::chrono::steady_clock::now();
        for (size_t i = 0; i != query_count; ++i)
        {
            results_eytzinger[i]=search(targets[i]);
        }
        auto end = std::chrono::steady_clock::now();
        std::chrono::duration<double> elapsed_seconds = end-start;
        std::cout << "elapsed time: " << elapsed_seconds.count() << "s\n";
    }
    {
        auto start = std::chrono::steady_clock::now();
        for (size_t i = 0; i != query_count; ++i)
        {
            results_lower_bound[i]=std::lower_bound(std::begin(a),std::end(a),targets[i])-std::begin(a);
        }
        auto end = std::chrono::steady_clock::now();
        std::chrono::duration<double> elapsed_seconds = end-start;
        std::cout << "elapsed time: " << elapsed_seconds.count() << "s\n";
    }
    for (size_t i = 0; i != query_count; ++i)
    {
        assert(a[results_lower_bound[i]]==b[results_eytzinger[i]]);   
    }
}

image

Dominant language
C++
Stars
334
Forks
76
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from boostorg/histogram

All issues in boostorg/histogram

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.