处理大量数据(千兆字节)我使用数据数组的索引。由于访问数据可能会导致缓存效率低下,因此我想将数组中的一些数据与索引一起缓存,这可以显着提高通过索引的操作速度。
缓存数据量是编译时选择,应包括零量缓存数据。我有大量索引,因此在这种情况下,我不想像
std::array
那样为额外的“空”元素付费。
所以,我做了一个专门化的模板:
using index_t = unsigned int;
using lexem_t = unsigned int;
template <std::size_t t_arg_cache_line_size>
struct lexem_index_with_cache_t {
index_t index;
std::array<lexem_t, t_arg_cache_line_size> cache_line;
constexpr std::size_t cache_line_size() const {
return t_arg_cache_line_size;
}
};
template<>
struct lexem_index_with_cache_t<0> {
index_t index;
static std::array<lexem_t, 0> cache_line;
constexpr std::size_t cache_line_size() const {
return 0;
}
};
std::array<lexem_t, 0> lexem_index_with_cache_t<0>::cache_line;
问题是我在零大小的专业化中使用的这个“黑客”,它利用静态成员来提供对
cache_line
的正式访问,而它是空的并且实际上并不需要访问。这使我可以避免使用此模板的函数的专业化,如下所示:
using lexem_index_with_cache = lexem_index_with_cache_t<0>;
template <typename T>
class seq_forward_comparator_cached
{
const std::vector<T>& vec;
public:
seq_forward_comparator_cached(const std::vector<T>& vec) : vec(vec) { }
bool operator() (const lexem_index_with_cache& idx1, const lexem_index_with_cache& idx2)
{
if (idx1.index == idx2.index) {
return false;
}
const auto it1_cache_line = idx1.cache_line; // This code wouldn’t compile in absence of static “hack”
const auto it2_cache_line = idx2.cache_line; // This code wouldn’t compile in absence of static “hack”
auto res = std::lexicographical_compare_three_way(
it1_cache_line.begin(), it1_cache_line.end(),
it2_cache_line.begin(), it2_cache_line.end());
if (res == std::strong_ordering::equal) {
auto range1 = std::ranges::subrange(vec.begin() + idx1.index + idx1.cache_line_size(), vec.end());
auto range2 = std::ranges::subrange(vec.begin() + idx2.index + idx2.cache_line_size(), vec.end());
return std::ranges::lexicographical_compare(range1, range2);
}
return res == std::strong_ordering::less;
}
};
当然,我可以实现该模板的另一个模板特化以实现零大小缓存,但这会导致代码重复,而且我有很多这样的函数,所以我不想特化所有这些。
现代 C++ 中避免这种
static
hack 和可能的代码重复的正确方法是什么?
我不确定,也许根据类型包含某种条件代码可能会有所帮助。
我想避免将对
cache_line
的访问包装到函数中,但如果这是唯一的情况,请提供有关方法的线索。
可编译的代码是这里。
我添加了一个示例,其中包含在非模板代码中使用
if constexpr(...)
的技巧。
#include <array>
using data_type = int;
template<size_t _data_size>
class ExtendableIndex
{
public:
constexpr static size_t data_size = _data_size;
data_type& at(size_t idx) { return data[idx]; }
size_t index;
std::array<data_type, _data_size> data;
};
template<>
class ExtendableIndex<0>
{
public:
constexpr static size_t data_size = 0;
data_type& at(size_t idx);
size_t index;
};
using DefaultIndex = ExtendableIndex<0>;
class DataUser
{
public:
void process(DefaultIndex& index)
{
if constexpr (DefaultIndex::data_size > 0)
{
// auto value = index.data[0]; // -> this fails to compile
auto value = index.at(0); // -> but this slight workaround solves the issue, `at()` is not implemented and thats OK.
}
}
template<size_t _data_size>
void process_template(ExtendableIndex<_data_size>& index)
{
if constexpr (DefaultIndex::data_size > 0)
{
auto value = index.data[0]; // -> this compiles even if index.data doesn't exist when 'process' is a template
}
}
};
int main()
{
DataUser r;
ExtendableIndex<0> index_zero;
r.process(index_zero);
r.process_template(index_zero);
ExtendableIndex<1> index_one;
r.process_template(index_one);
}