Ale*_*lec 11 c++ algorithm data-structures
我认为这是一个相当常见的问题,但我似乎无法通过谷歌搜索找到答案(也许有一个更准确的名称,我不知道的问题?)
您需要使用"hit()"方法实现一个结构,该方法用于报告命中和hitsInLastSecond | Minute | Hour方法.你有一个具有纳秒准确度的计时器.你如何有效地实现这一目标?
我的想法是这样的(在psuedo-C++中)
class HitCounter {
void hit() {
hits_at[now()] = ++last_count;
}
int hitsInLastSecond() {
auto before_count = hits_at.lower_bound(now() - 1 * second)
if (before_count == hits_at.end()) { return last_count; }
return last_count - before_count->second;
}
// etc for Minute, Hour
map<time_point, int> hits_at;
int last_count = 0;
};
Run Code Online (Sandbox Code Playgroud)
这有用吗?好吗?有更好的东西吗?
更新:添加修剪并根据评论切换到双端队列:
class HitCounter {
void hit() {
hits.push_back(make_pair(now(), ++last_count));
}
int hitsInLastSecond() {
auto before = lower_bound(hits.begin(), hits.end(), make_pair(now() - 1 * second, -1));
if (before == hits.end()) { return last_count; }
return last_count - before_count->second;
}
// etc for Minute, Hour
void prune() {
auto old = upper_bound(hits.begin(). hits.end(), make_pair(now - 1 * hour, -1));
if (old != hits.end()) {
hits.erase(hits.begin(), old)
}
}
deqeue<pair<time_point, int>> hits;
int last_count = 0;
};
Run Code Online (Sandbox Code Playgroud)
您所描述的称为直方图。
如果您想要纳秒级的精度,那么使用哈希会消耗大量的 CPU 资源。您可能需要一个环形缓冲区来存储数据。
使用 std::chrono 来实现您需要的计时精度,但坦率地说,每秒点击数似乎是您需要的最高粒度,如果您着眼于整体大局,那么精度是多少似乎并不那么重要。
这是您如何进行操作的部分介绍性示例:
#include <array>
#include <algorithm>
template<size_t RingSize>
class Histogram
{
std::array<size_t, RingSize> m_ringBuffer;
size_t m_total;
size_t m_position;
public:
Histogram() : m_total(0)
{
std::fill_n(m_ringBuffer.begin(), RingSize, 0);
}
void addHit()
{
++m_ringBuffer[m_position];
++m_total;
}
void incrementPosition()
{
if (++m_position >= RingSize)
m_position = 0;
m_total -= m_ringBuffer[m_position];
m_ringBuffer[m_position] = 0;
}
double runningAverage() const
{
return (double)m_total / (double)RingSize;
}
size_t runningTotal() const { return m_total; }
};
Histogram<60> secondsHisto;
Histogram<60> minutesHisto;
Histogram<24> hoursHisto;
Histogram<7> weeksHisto;
Run Code Online (Sandbox Code Playgroud)
这是一个幼稚的实现,假设您每秒调用它并递增位置,并且每个 RingSize 将 runningTotal 从一个直方图转置到下一个直方图(因此每 60 秒将 secondaryHisto.runningTotal 添加到 分钟Histo)。
希望这将是一个有用的介绍性起点。
如果您想跟踪每秒点击次数的更长直方图,您可以使用此模型来实现这一点,通过增加环大小,添加第二个总计来跟踪最后 N 个环缓冲区条目,以便 m_subTotal = sum(m_ringBuffer[m_position - N .. m_position]),类似于 m_total 的工作方式。
size_t m_10sTotal;
...
void addHit()
{
++m_ringBuffer[m_position];
++m_total;
++m_10sTotal;
}
void incrementPosition()
{
// subtract data from >10 sample intervals ago.
m_10sTotal -= m_ringBuffer[(m_position + RingBufferSize - 10) % RingBufferSize];
// for the naive total, do the subtraction after we
// advance position, since it will coincide with the
// location of the value RingBufferSize ago.
if (++m_position >= RingBufferSize)
m_position = 0;
m_total -= m_ringBuffer[m_position];
}
Run Code Online (Sandbox Code Playgroud)
您不必将直方图设置为这些尺寸,这只是一个简单的抓取模型。有多种替代方案,例如同时递增每个直方图:
secondsHisto.addHit();
minutesHisto.addHit();
hoursHisto.addHit();
weeksHisto.addHit();
Run Code Online (Sandbox Code Playgroud)
每个都独立滚动,因此都有当前值。根据您希望以该粒度返回的数据来调整每个历史记录的大小。