C++ Fast std::vector<;布尔>;重置
我有一个非常大的位向量(百万“存在”位的10s),其大小仅在运行时已知(即noC++ Fast std::vector<;布尔>;重置,c++,vector,boolean,C++,Vector,Boolean,我有一个非常大的位向量(百万“存在”位的10s),其大小仅在运行时已知(即nostd::bitset),但在实际使用之前已知,因此可以预先分配容器 该向量最初为全零,以增量、稀疏和随机方式设置单个位。这个容器的唯一用途是直接随机访问检查“存在”(无STL)。 在尝试了几种替代容器之后,似乎std::vector非常适合我的需要(尽管它在概念上存在问题) 每隔一段时间,我需要重置此向量中的所有位。 因为它太大了,我不能完全重置它的所有元素。但是,我知道所有设置位的索引,所以我可以单独重置它们 由于
std::bitset
),但在实际使用之前已知,因此可以预先分配容器
该向量最初为全零,以增量、稀疏和随机方式设置单个位。这个容器的唯一用途是直接随机访问检查“存在”(无STL)。
在尝试了几种替代容器之后,似乎std::vector
非常适合我的需要(尽管它在概念上存在问题)
每隔一段时间,我需要重置此向量中的所有位。因为它太大了,我不能完全重置它的所有元素。但是,我知道所有设置位的索引,所以我可以单独重置它们 由于
std::vector
将bool
s表示为位,因此每次此类重置都涉及额外移位和其他此类调整操作
是否有一种(便携式)方法可以进行“粗略的”、低精度的重置?一个可以重置我请求的位所属的整个整数,从而避免任何额外操作的方法 是否有(便携式)方法进行“粗略”、低精度的重置? 一个可以重置我请求的位所属的整个整数,从而避免任何额外操作的方法 否和否(不适用于该容器类型) FWIW,我写了一个数据类型,它可以帮助您处理大型稀疏位集(您需要将它包装在一个外部类型中,以创建它们的数组等)。该类型为32位宽,通过使用32位(最坏情况=>3位设置)存储指向
向量的指针,或(通常)使用32位作为2位大小/计数和最多3个嵌入式10位设置位索引,跟踪1024位的开/关状态。如果不需要向量
,则与std::bitset
相比,存储密度提高了32倍,这将有助于减少内存缓存未命中
注意:在向量*
中,大小成员-在中的最低有效位上对齐是至关重要的,这样任何合法指针(至少有4字节对齐)在中的值都必须为0。为32位应用程序编写,其中uintpttr\u t==32
,但相同的概念可用于创建64位版本;数据类型需要使用uintptr\t
,以便在需要时存储指向-向量的指针,因此必须匹配32位或64位执行模式
代码包括显示随机操作影响位集
s和位打包器32
s的测试
#include <iostream>
#include <bitset>
#include <vector>
#include <algorithm>
#include <set>
class Bit_Packer_32
{
// Invariants:
// - 0 bits set: u_ == p_ == a == b == c == 0
// - 1 bit set: in_use == 1, a meaningful, b == c == 0
// - 2 bits set: in_use == 2, a & b meaningful, c == 0
// - 3 bits set: in_use == 3, a & b & c meaningful
// - >3 bits: in_use == 0, p_ != 0
// NOTE: differentiating 0 from >3 depends on a == b == c == 0 for former
public:
Bit_Packer_32() : u_(0) { }
class Reference
{
public:
Reference(Bit_Packer_32& p, size_t n) : p_(p), n_(n) { }
Reference& operator=(bool b) { p_.set(n_, b); return *this; }
operator bool() const { return p_[n_]; }
private:
Bit_Packer_32& p_;
size_t n_;
};
Reference operator[](size_t n) { return Reference(*this, n); }
void set(size_t n)
{
switch (in_use)
{
case 0:
if (p_)
{
if (std::find(p_->begin(), p_->end(), n) == p_->end())
p_->push_back(n);
}
else
{ in_use = 1; a = n; }
break;
case 1: if (a != n) { in_use = 2; b = n; } break;
case 2: if (a != n && b != n) { in_use = 3; c = n; } break;
case 3: if (a == n || b == n || c == n) break;
V* p = new V(4);
(*p)[0] = a; (*p)[1] = b; (*p)[2] = c; (*p)[3] = n;
p_ = p;
}
}
void reset(size_t n)
{
switch (in_use)
{
case 0:
if (p_)
{
V::iterator i = std::find(p_->begin(), p_->end(), n);
if (i != p_->end())
{
p_->erase(i);
if (p_->size() == 3)
{
// faster to copy out w/o erase, but tedious
int p0 = (*p_)[0], p1 = (*p_)[1], p2 = (*p_)[2];
delete p_;
a = p0; b = p1; c = p2;
in_use = 3;
}
}
}
break;
case 1: if (a == n) { u_ = 0; /* in_use = a = 0 */ } break;
case 2: if (a == n) { in_use = 1; a = b; b = 0; break; }
else if (b == n) { in_use = 1; b = 0; break; }
case 3: if (a == n) a = c;
else if (b == n) b = c;
else if (c == n) ;
else break;
in_use = 2;
c = 0;
}
}
void reset_all()
{
if (in_use == 0) delete p_;
u_ = 0;
}
size_t count() const { return in_use ? in_use : p_ ? p_->size() : 0; }
void set(size_t n, bool b) { if (b) set(n); else reset(n); }
bool operator[](size_t n) const
{
switch (in_use)
{
case 0:
return p_ && std::find(p_->begin(), p_->end(), n) != p_->end();
case 1: return n == a;
case 2: return n == a || n == b;
case 3: return n == a || n == b || n == c;
}
}
// e.g. operate_on<std::bitset<1024>, Op_Set>()
// operate_on<std::set<unsigned>, Op_Insert>()
// operate_on<std::vector<unsigned>, Op_Push_Back>()
template <typename T, typename Op>
T operate_on(const Op& op = Op()) const
{
T result;
switch (in_use)
{
case 0:
if (p_)
for (V::const_iterator i = p_->begin(); i != p_->end(); ++i)
op(result, *i);
break;
case 3: op(result, c);
case 2: op(result, b);
case 1: op(result, a);
}
return result;
}
private:
union
{
uintptr_t u_;
typedef std::vector<unsigned> V;
V* p_;
struct
{
unsigned in_use : 2;
unsigned a : 10;
unsigned b : 10;
unsigned c : 10;
};
};
};
struct Op_Insert
{
template <typename T, typename U>
void operator()(T& t, const U& u) const { t.insert(u); }
};
struct Op_Set
{
template <typename T, typename U>
void operator()(T& t, const U& u) const { t.set(u); }
};
struct Op_Push_Back
{
template <typename T, typename U>
void operator()(T& t, const U& u) const { t.push_back(u); }
};
#define TEST(A, B, MSG) \
do { \
bool pass = (A) == (B); \
if (pass) break; \
std::cout << "@" << __LINE__ << ": (" #A " == " #B ") "; \
std::cout << (pass ? "pass\n" : "FAIL\n"); \
std::cout << " (" << (A) << " ==\n"; \
std::cout << " " << (B) << ")\n"; \
std::cout << MSG << std::endl; \
} while (false)
template <size_t N>
std::set<unsigned> to_set(const std::bitset<N>& bs)
{
std::set<unsigned> result;
for (unsigned i = 0; i < N; ++i)
if (bs[i]) result.insert(i);
return result;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::set<T>& s)
{
for (std::set<T>::const_iterator i = s.begin(); i != s.end(); ++i)
{
if (i != s.begin()) os << ' ';
os << *i;
}
return os;
}
int main()
{
TEST(sizeof(uintptr_t), 4, "designed for 32-bit uintptr_t");
for (int i = 0; i < 100000; ++i)
{
Bit_Packer_32 bp;
std::bitset<1024> bs;
for (int j = 0; j < 1 + i % 10; ++j)
{
int n = rand() % 1024;
int v = rand() % 2;
bs[n] = v;
bp[n] = v;
// TEST(bp.get_bitset(), bs);
TEST((bp.operate_on<std::set<unsigned>, Op_Insert>()), to_set(bs),
"j " << j << ", n " << n << ", v " << v);
}
}
}
#包括
#包括
#包括
#包括
#包括
钻头类封隔器类32
{
//不变量:
//-0位集:u_==p_==a==b==c==0
//-1位集:in_use==1,a有意义,b==c==0
//-2位设置:in_use==2,a&b有意义,c==0
//-3位设置:在用==3,a&b&c有意义
//->3位:使用中==0,p.=0
//注:区分0和>3取决于前者的a==b==c==0
公众:
位包装器32():u(0){
课堂参考
{
公众:
参考(钻头封隔器32&p,尺寸n):p(p),n(n){
引用和运算符=(bool b){p_u.set(n_u,b);返回*this;}
运算符bool()常量{return p_[n_];}
私人:
钻头封隔器32&p;
大小;
};
引用运算符[](size_t n){返回引用(*this,n);}
无效集(大小\u t n)
{
开关(正在使用)
{
案例0:
如果(p_)
{
如果(std::find(p_->begin(),p_->end(),n)==p_->end())
p_->推回(n);
}
其他的
{in_use=1;a=n;}
打破
案例1:如果(a!=n){in_use=2;b=n;}中断;
案例2:如果(a!=n&&b!=n){in_use=3;c=n;}中断;
案例3:如果(a==n | | b==n | | c==n)中断;
V*p=新的V(4);
(*p)[0]=a;(*p)[1]=b;(*p)[2]=c;(*p)[3]=n;
p=p;
}
}
无效重置(大小\u t n)
{
开关(正在使用)
{
案例0:
如果(p_)
{
迭代器i=std::find(p_->begin(),p_->end(),n);
如果(i!=p->end())
{
p_->擦除(i);
如果(p->size()==3)
{
//无需擦除即可更快地复制,但繁琐
int p0=(*p_)[0],p1=(*p_)[1],p2=(*p_)[2];
删除p_;
a=p0;b=p1;c=p2;
使用中=3;
}
}
}
打破
案例1:如果(a==n){u\u=0;/*in\u use=a=0*/}中断;
案例2:如果(a==n){in_use=1;a=b;b=0;break;}
如果(b==n){in_use=1;b=0;break;}
案例3:如果(a==n)a=c;
如果(b==n)b=c;
else如果(c==n);
否则就断了;
在用=2;
c=0;
}
}
void reset_all()
{
如果(in_use==0)删除p_;
u_uu=0;
}
size\u t count()常量{return in\u use?in\u use:p\u?p\u->size():0;}
无效集(大小n,布尔b){if(b)set(n);else reset(n);}
布尔运算符[](大小)常量
{
开关(正在使用)
{
案例0:
返回p_&&std::find(p_->begin(),p_->end(),n)!=p_->end();
案例1:返回n==a;
案例2:返回n==a | | n==b;
案例3:返回n==a | | n==b | | n==c;
}
}
//例如,在()上操作
//操作
//操作
样板
T操作(const Op&Op=Op())const
{
T结果;
开关(正在使用)
class DynamicBitset
{
private:
static const unsigned BITS_LEN = 64; // or 32, or more?
typedef std::bitset<BITS_LEN> Bits;
std::vector<Bits> data_;
public:
DynamicBitset(size_t len=0) {
resize(len);
}
// reset all bits to false
void reset() {
for(auto it=data_.begin(); it!=data_.end(); ++it) {
it->reset(); // we can use the fast bitfield::reset :)
}
}
// reset the whole bitset belonging to bit i
void reset_approx(size_t i) {
data_[i/BITS_LEN].reset();
}
// make room for len bits
void resize(size_t len) {
data_.resize(len/BITS_LEN + 1);
}
// access a bit
Bits::reference operator[](size_t i) {
size_t k = i/BITS_LEN;
return data_[k][i-k*BITS_LEN];
}
bool operator[](size_t i) const {
size_t k = i/BITS_LEN;
return data_[k][i-k*BITS_LEN];
}
};
class SparseDynamicBitset
{
private:
static const unsigned BITS_LEN = 64; // ?
typedef std::bitset<BITS_LEN> Bits;
std::map<unsigned,Bits> data_;
public:
// reset all "stored" bits to false
void reset() {
for(auto it=data_.begin(); it!=data_.end(); ++it) {
it->second.reset(); // uses bitfield::reset :)
}
}
// access a bit
Bits::reference operator[](size_t i) {
size_t k = i/BITS_LEN;
return data_[k][i-k*BITS_LEN];
}
bool operator[](size_t i) const {
size_t k = i/BITS_LEN;
auto it = data_.find(k);
if(it == it.end()) {
return false; // the default
}
return it->second[i-k*BITS_LEN];
}
};