仅关心速度时,如何存储二进制数据

How to store binary data when you only care about speed?

本文关键字:存储 二进制 数据 何存储 速度 关心      更新时间:2023-10-16

我在d维度中有n个点,假设n是100万和d 100。我所有的观点都有二进制坐标,即{0,1}^d,我只对 speed 感兴趣。

当前我的实现使用std::vector<int>。我想知道是否可以通过更改数据结构来从更快的执行方面受益。我只是在进行插入和搜索(我不更改位)。

我发现的所有相关问题std::vector<char>std::vector<bool>std::bitset,但所有提及的空间都应该通过使用此类结构来获得。

对于C 的二进制数据,当速度是主要问题时,适当的数据结构是什么?

中的二进制数据

我打算用二进制数据填充我的数据结构,然后进行大量连续搜索(我的意思是我真的不在乎一个点的i-th坐标,如果我访问了一个点,我将连续访问其所有坐标)。我将彼此计算锤距

如果值独立,均匀分布,并且您想找到两个独立,随机选择的点之间的锤击距离,则最有效的布局是一个堆积的位阵列。

理想情况下,这个包装的阵列将分为最大的块尺寸,您的popcnt指令可以使用:64位。锤距离是popcnt(x_blocks[i] ^ y_blocks[i])的总和。在具有有效非对齐访问的处理器上,与未对准读取的字节对齐可能是最有效的。在未对准的处理器上受到惩罚的处理器,应该考虑对排列行的内存开销是否值得更快。

参考的局部性可能是驱动力。因此,很明显,您将单点的D坐标表示为连续的BitVector。std::bitset<D>将是一个合乎逻辑的选择。

但是,要意识到的下一个重要的事情是,您可以轻松地看到当地的好处,高达4KB。这意味着您不应该选择一个点并将其与所有其他N-1点进行比较。相反,小组分别为4KB,并比较这些组。两种方式都是O(N*N),但第二种方式会更快。

您可以通过使用三角形不等式-Hamming(a,b)+Hamming(b,c) >= Hamming (a,c)击败O(N*N)。我只是想知道如何。这可能取决于您想要输出的方式。天真的输出将是一组距离,这是不可避免的O(N*N)

我写了一个简单的程序,以填充和连续访问数据结构与二进制数据:

  1. std::vector<int>
  2. std::vector<char>
  3. std::vector<bool>
  4. std::bitset

我使用了时间测量。我使用-O3优化标志,n = 1 mil和d = 100。

这是向量的代码:

#include <vector>
#include <iostream>
#include <random>
#include <cmath>
#include <numeric>
#include <functional> //plus, equal_to, not2
#include <ctime>
#include <ratio>
#include <chrono>
#define T int
unsigned int hd(const std::vector<T>& s1, const std::vector<T>::iterator s2)
{
    return std::inner_product(
        s1.begin(), s1.end(), s2, 
        0, std::plus<unsigned int>(),
        std::not2(std::equal_to<std::vector<T>::value_type>())
    );
}

std::uniform_int_distribution<int> uni_bit_distribution(0, 1);
std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
// g++ -Wall -O3 bitint.cpp -o bitint
int main()
{
    const int N = 1000000;
    const int D = 100;
    unsigned int hamming_dist[N] = {0};
    unsigned int ham_d[N] = {0};
    std::vector<T> q;
    for(int i = 0; i < D; ++i)
        q.push_back(uni_bit_distribution(generator));
    using namespace std::chrono;
    high_resolution_clock::time_point t1 = high_resolution_clock::now();

    std::vector<T> v;
    v.resize(N * D);
    for(int i = 0; i < N; ++i)
        for(int j = 0; j < D; ++j)
            v[j + i * D] = uni_bit_distribution(generator);

    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    duration<double> time_span = duration_cast<duration<double> >(t2 - t1);
    std::cout << "Build " << time_span.count() << " seconds.n";
    t1 = high_resolution_clock::now();
    for(int i = 0; i < N; ++i)
        for(int j = 0; j < D; ++j)
        hamming_dist[i] += (v[j + i * D] != q[j]);
    t2 = high_resolution_clock::now();
    time_span = duration_cast<duration<double> >(t2 - t1);
    std::cout << "No function hamming distance " << time_span.count() << " seconds.n";
    t1 = high_resolution_clock::now();
    for(int i = 0; i < N; ++i)
        ham_d[i] = hd(q, v.begin() + (i * D));
    t2 = high_resolution_clock::now();
    time_span = duration_cast<duration<double> >(t2 - t1);
    std::cout << "Yes function hamming distance " << time_span.count() << " seconds.n";
    return 0;
}

std::bitset的代码可以在以下方式中找到:XOR BITSET将2D bitset存储为1d

对于std::vector<int>,我得到了:

Build 3.80404 seconds.
No function hamming distance 0.0322335 seconds.
Yes function hamming distance 0.0352869 seconds.

对于std::vector<char>,我得到了:

Build 8.2e-07 seconds.
No function hamming distance 8.4e-08 seconds.
Yes function hamming distance 2.01e-07 seconds.

对于std::vector<bool>,我得到了:

Build 4.34496 seconds.
No function hamming distance 0.162005 seconds.
Yes function hamming distance 0.258315 seconds.

对于std:bitset,我得到了:

Build 4.28947 seconds.
Hamming distance 0.00385685 seconds.

std::vector<char>似乎是赢家。