计算每秒的事件率

Calculate event rate per second

我有一个包含数百万个事件的游戏文件,文件大小可以 > 10gb

每一行都是一个游戏动作,例如:

player 1, action=kill, timestamp=xxxx(ms granularity)
player 1, action=jump, timestamp=xxxx
player 2, action=fire, timestamp=xxxx

对于这个数据集,每个动作都是唯一且有限的。

我想对此文件进行分析,例如每秒的事件总数,同时跟踪那一秒内的单个操作数。

我的半伪代码计划:

lastReadGameEventTime = DateTime.MinValue;

while(line=getNextLine() != null)
{
   parse_values(lastReadGameEventTime, out var timestamp, out var action);
   if(timestamp ==  MinValue)
   {
      lastReadGameEventTime = timestamp;
   }
   else if(timestamp.subtract(lastReadGameEventTime).TotalSeconds > 1)
   {
      notify_points_for_this_second(datapoints);
      datapoints = new T();
   }

   if(!datapoints.TryGetValue(action, out var act))
      act = new Dictionary<string,int>();
      act[action] = 0;
   else
      act[action]++;
}
  lastReadGameEventTime = parse_time(line)

我担心的是这太天真了。我在想也许可以计算整分钟并获得每秒的平均值。但我当然会错过游戏事件峰值。 如果我想计算 5 天的平均值,它会进一步降低结果集。 有什么妙招吗?

您在这里问了几个不同的问题。都是相关的。您的要求不是很详细,但我想我可以为您指明正确的方向。我将假设您想要的只是过去某个时期每秒发生的事件数。因此,我们所需要的只是某种方法来保存该期间每一秒的整数(事件计数)。

一天有 86,400 秒。假设您需要 10 天的信息。您可以构建一个大小为 864,000 的循环缓冲区来保存 10 天的计数:

const int SecondsPerDay = 86400;
const int TenDays = 10 * SecondsPerDay;

int[] TenDaysEvents = new int[TenDays];

所以您总是有最近 10 天的计数。

假设您有一个事件处理程序来读取您的套接字数据并将信息传递给一个函数,您可以轻松地更新您的数据:

DateTime lastEventTime = DateTime.MinValue;
int lastTimeIndex = 0;

void ProcessReceivedEvent(string event)
{
    // here, parse the event string to get the DateTime
    DateTime eventTime = GetEventDate(event);
    if (lastEventTime == DateTime.MinValue)
    {
        lastTimeIndex = 0;
    }
    else if (eventTime != lastEventTime)
    {
        // get number of seconds since last event
        var elapsedTime = eventTime - lastEventTime;
        var elapsedSeconds = (int)elapsedTime.TotalSeconds;

        // For each of those seconds, set the number of events to 0
        for (int i = 1; i <= elapsedSeconds; ++i)
        {
            lastTimeIndex = (lastTimeIndex + 1) % TenDays; // wrap around if we get past the end
            TenDaysEvents[lastTimeIndex] = 0;
        }
    }
    // Now increment the count for the current time index
    ++TenDaysEvents[lastTimeIndex];
}

这会始终将最近 10 天保存在内存中,并且易于更新。报告有点困难,因为起点可能在数组的中间。也就是说,如果当前索引是469301,那么开始时间是469302。这是一个循环缓冲区。对此进行报告的天真的方法是将循环缓冲区复制到另一个数组或列表,起点位于新集合中的位置 0,然后对其进行报告。或者,您可以编写一个自定义枚举器,从当前位置开始倒计时。这不会特别难创建。

上面的美妙之处在于您的数组保持静态。您分配一次,然后重新使用它。不过,您可能想要额外添加 60 个条目,以便当前时间和 10 天前的时间之间有一些 "buffer"。这将防止 10 天前的数据在查询期间被更改。添加额外的 300 个项目,给自己一个 5 分钟的缓冲时间。

另一种选择是创建条目链接列表。同样,每秒一个。这样,您就可以将项目添加到列表的末尾,并从前面删除较旧的项目。每当新的一秒出现事件时,将事件条目添加到列表的末尾,然后从列表的前面删除超过 10 天(或任何阈值)的条目。您仍然可以按照另一个答案中的建议使用 LINQ 来报告事物。

你也可以使用混合动力车。随着每一秒的流逝,将记录写入数据库,并在内存中保留最后一分钟、一小时或其他任何内容。这样,您就可以在内存中获得最新的数据,用于快速报告和实时更新,但您也可以使用数据库报告自您第一次开始收集数据以来的任何时期。

无论您做出什么决定,您可能都应该保留某种数据库,因为您无法保证您的系统不会宕机。事实上,您几乎可以保证您的系统 在某个时候崩溃。丢失数据或必须扫描数 TB 的日志数据以重建您随时间收集的数据并不有趣。