我的任务是创建一个数据结构,在过去24小时的每一分钟都保存一个布尔值。 (事件X发生了吗?)我需要一直保持最后24小时。 (即,将不断添加数据,弹出旧数据。)数据将持久保存到闪存驱动器。我们在嵌入式平台上,但内存不是那么有限(我有128MB可用),但碎片可能会成为一个问题。这是一个实时系统,但由于记录是每分钟,因此几乎没有运行时限制。
界面看起来像这样:
class x_record {
public:
// record whether or not x occurred this minute
void record_entry(bool x_occured);
// how many minutes has x occured in the last 24hrs?
unsigned int x_occurance_minutes() const;
private:
// HERE BE DRAGONS
};
用于存储实际数据的良好数据结构是什么?我目前最喜欢的是std::deque<bool>
和一个24 long long
的数组,其中64个中的60个每个用于60分钟的一小时。后者是目前持久性的最爱。
我认为我对这两种想法的利弊都有很好的了解,但希望你们中的一些人可以提供更多内容,甚至更多的想法。
P.S。:这是严格的C ++ 03 + TR1 + Boost 1.52,没有C ++ 11/14可用。
答案 0 :(得分:9)
要详细说明vector<bool>
版本,我认为这是一个非常好的主意,因为您总是存储相同数量的数据(至少是这样的)我的理解):
class x_record {
vector<bool> data;
unsigned int currentMinute;
public:
x_record(): data(24*60, false), currentMinute(0){}
// record whether or not x occurred this minute
void record_entry(bool x_occured){
data[currentMinute] = x_occured;
currentMinute = (currentMinute+1)%data.size();
}
};
这样,矢量大小是恒定的,因此不应该被分段(因为它是同时分配的)。您可以使用currentMinute
变量跟踪当前分钟。当您填写所有字段时,只需将0
设置为%(24*60)
并覆盖旧数据,因为您不需要它。
您也可以使用普通的数组而不是vector<bool>
,但这需要更多空间(因为通常C ++存储bool
值的方式与{ {1}}),或者当我们得到char
专业化时,在我看来 - 重新发明轮子的一些操作。
答案 1 :(得分:6)
循环缓冲区:
int countBits(std::uint32_t v) {
// source: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
typedef std::uint32_t T;
v = v - ((v >> 1) & (T)~(T)0/3); // temp
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * CHAR_BIT; // count
}
class x_record {
public:
x_record() { std::memset(&history, 0, sizeof(history)); }
// record whether or not x occurred this minute
void record_entry(bool x_occured) {
uint64_t mask = 1 << (bit % 32);
uint64_t set = x_occured ? mask : 0;
history[bit / 32] = (history[bit / 32] & ~mask) | set;
bit = (bit + 1) % (24*60);
}
// how many minutes has x occured in the last 24hrs?
int x_occurance_minutes() const {
int count = 0;
for (int i=0; i<45; ++i) {
count += countBits(history[i]);
}
return count;
}
private:
std::uint32_t history[45];
short bit = 0;
};
答案 2 :(得分:3)
我每小时都会有一个std::vector<bool>
,而且每小时只有一次。所以你可以拥有std::deque<std::vector<bool> >
。同样,它可能是std::deque<long long>
,但与向量相比,我看不到任何好处。
它使事情变得高效,易于理解且不易出错。
答案 3 :(得分:3)
正如评论中所建议的,std::bitset
可能是一个不错的选择。它是一个固定大小的位序列,可以独立操作。它比std::vector<bool>
占用更少的内存(甚至认为你说这对你来说不是问题)。但是,如果您需要将序列设置为循环,则可能需要将其包装在另一个容器中,以便始终保持最后24 * 60分钟,而不是一天中的24 * 60分钟。
答案 4 :(得分:1)
如果您只担心事件在过去24小时内发生的频率,并且可以完全忽略事件发生的时间,您只需记录事件发生的时间。
考虑以下(未经测试):
class x_record {
public:
// record whether or not x occurred this minute
void record_entry(bool x_occured) {
if (x_occured) {
m_records.insert(getTime());
}
}
// how many minutes has x occured in the last 24hrs?
unsigned int x_occurance_minutes() {
clearRecords();
return m_records.size();
}
private:
time_t getTime() const {
return time(NULL) / 60; // Get Minute time stamp
}
void clearRecords() {
// Erase all records that happend before the last 24 hours
time_t frameStart = getTime() - 60*60*24;
for (std::set<time_t>::iterator it = m_recods.begin(); it != m_records.end(); ++it) {
if (*it < frameStart) {
m_records.erase(it);
} else {
break;
}
}
}
private:
std::set<time_t> m_records;
};
如果事件稀疏发生,这是最合适的。
它使用约束来设置以严格的弱顺序存储它们的元素,以便首先列出具有较低时间戳的元素。还
您应该考虑使用不同的密钥类型插入集合,因为time_t
不能保证代表秒。
答案 5 :(得分:1)
我建议使用一个包含boost.dynamic_bitset的类,一个用于处理新值存储的计数器和一个用于通过小时/分钟访问的转换函数。
dynamic_set处理大部分要求:
答案 6 :(得分:1)
正如Marius Bancila首先提出in a comment然后in an answer所建议的那样(请尽快给出答案,他给出了原来是解决方案的提示),std::bitset<>
是理想的为了这。但是,由于他的回答相当模糊,我决定对我最终使用的内容进行更具体的描述:
class x_record {
public:
void record_entry(bool x_occured) {
record_ <<= 1;
record_[0] = x_occurred;
}
unsigned int x_occurance_minutes() const {
return record_.count();
}
void write_to(std::ostream& os) const {
os << m_overload_minutes.to_string();
}
void read_from(std::istream& is) const {
std::string bits;
is >> std::setw(minutes_to_keep) >> bits;
if( !is || bits.size()!=minutes_to_keep )
throw std::runtime_error("invalid file format");
record_ = std::bitset<60*24>(bits);
}
private:
std::bitset<60*24> record_;
};
正如您所看到的,std::bitset<>
正是我所需要的。此外,持久化数据非常容易。
当然,实际上,这是一个更复杂的 1 ,但原则上这确实是整个事情。
感谢大家帮助我!
1 事实证明,每隔几个msecs就更容易调用x_occurance_minutes()
,record_.count()
似乎有一些开销,所以我在{{1}中调用它(仅每分钟调用一次)代替并缓存结果。