简体   繁体   English

计算每秒调用该方法的次数的最佳方法

[英]best way to count how many times per second the method is called

I have dll method that should be "QoSed" - this method should be called maximum 100 times per second.:我有一个 dll 方法应该是“QoSed”——这个方法每秒最多调用 100 次。:

    private static extern int ExecTrans(int connectionId);

This method used only in one place in program so it's ok to qos this place.这个方法只在程序的一个地方用过,这个地方qos是可以的。 I need separate "qos counter" for each connectionId .我需要为每个connectionId单独的“qos 计数器”。 So ExecTrans(1) and ExecTrans(2) should go to different counters.所以ExecTrans(1)ExecTrans(2)应该去不同的计数器。

At first iteration I would like to count how often method is called (for each connectionId ).在第一次迭代时,我想计算调用方法的频率(对于每个connectionId )。 Ie I want to have "live statistics".即我想要“实时统计”。 There are two approach:有两种方法:

- allow to exceed limitiation for a short period. for example allow "100 transaction from 0 to 1 second, 100 transaction from 1 to 2 seconds and 200 transactions from 0.5 to 1.5 second".
- at any second interval transactions should not exceed 100.

For now I don't care which of these methods to use but I would select one creating less "overhead".现在我不在乎使用这些方法中的哪一种,但我会选择一种减少“开销”的方法。 I want qos to add as less "extra-work" as possible because it's trading software sensitive to every 0.1 ms.我希望 qos 尽可能少地添加“额外工作”,因为它是对每 0.1 毫秒敏感的交易软件。

As for first approach I think I can use something like that (pseude-code, probably stats and curStats should be made thread-safe):至于第一种方法,我想我可以使用类似的方法(伪代码,可能statscurStats应该是线程安全的):

private int[] stats      // statistic to display to user
private int[] curStats;  // statistic currently collection

OnOneSecondElapsed(object source, ElapsedEventArgs args) {
    foreach (conId : connIds) {
        stats[conId] = curStats[conId];
        curStats[conId] = 0;
    }
}

myMethod {
    ......
    ExecTrans(conId);
    ++curStats[conId];
    ......
}

As for second approach.... is it possible to make a collection where objects life for exactly one second and after a second disappear?至于第二种方法......是否有可能制作一个集合,其中对象生命恰好一秒钟,一秒钟后消失? Then every time I will add the next object to a collection unless collection contains 100 objects.然后每次我都会将下一个对象添加到集合中,除非集合包含 100 个对象。

What do you think?你怎么认为? I'm not familar with C# library files so probably I'm missing some useful classess, probably you can suggest another approach.我不熟悉 C# 库文件,所以可能我错过了一些有用的类,也许你可以建议另一种方法。

A first approach:第一种方法:

  • Use a ConcurrentQueue<DateTime>使用ConcurrentQueue<DateTime>
  • Before every request, check the size of the queue.在每次请求之前,检查队列的大小。 If > 100, cancel the request如果 > 100,则取消请求
  • If < 100, enqueue the current DateTime and execute the request如果 < 100,则将当前 DateTime 入队并执行请求
  • In a background thread, every 0.1 second, remove entries older than 1 second在后台线程中,每 0.1 秒删除一次早于 1 秒的条目

It should be fairly efficient, but:它应该相当有效,但是:

  • Since there's no locking between the time you check the queue count and the time you enqueue, you may sometimes get slightly over 100 requests per second由于在检查队列计数和入队时间之间没有锁定,因此有时每秒可能会收到略多于 100 个请求
  • Since the background thread executes every 0.1 second, if you receive 100 requests at the same time, it may block the queue for up to 1.1 seconds.由于后台线程每 0.1 秒执行一次,如果您同时收到 100 个请求,它可能会阻塞队列长达 1.1 秒。 Adjust the sleep time as needed.根据需要调整睡眠时间。

I may be wrong but I don't think there's a perfect solution.我可能是错的,但我认为没有完美的解决方案。 Basically, the more precise the system is, the more overhead there is.基本上,系统越精确,开销就越大。 You have to adjust the parameters depending on your needs.您必须根据需要调整参数。

There is a tool called a profiler that does exactly what you are looking for.有一种称为分析器的工具可以完全满足您的需求。 You run it with your code and it will tell you exactly how much time it spent in each method and how many times each method was called.你用你的代码运行它,它会确切地告诉你它在每个方法中花费了多少时间以及每个方法被调用了多少次。 Here is an old thread about C# profilers.这是一个关于 C# 分析器的旧线程 If you're a professional developer you may have already have a company license to a profiler.如果您是专业开发人员,您可能已经拥有分析器的公司许可证。

In case someone needs to measure rather than throttling... here's a naive approach which gives you a rough estimate:万一有人需要测量而不是节流......这是一种天真的方法,可以为您提供粗略的估计:

class A{
    private int _calls;
    private Stopwatch _sw;

    public A(){
       _calls = 0;
       _sw = new Stopwatch();
       _sw.Start();
    }

    public void MethodToMeasure(){
        //Do stuff
        _calls++;
        if(sw.ElapsedMilliseconds > 1000){
            _sw.Stop();
            //Save or print _calls here before it's zeroed
            _calls = 0;
            _sw.Restart();
        }
    }
}

You will get called more than n times per second in some cases, I'm assuming you just don't want to do any actual processing in the extra cases.在某些情况下,您每秒会被调用超过 n 次,我假设您只是不想在额外的情况下进行任何实际处理。

You can use a synchronized Q object to hold transactions to be processed for each connection.您可以使用同步 Q 对象来保存要为每个连接处理的事务。 Calling your method would just enqueue the data saying what should be done.调用您的方法只会将数据排入队列,说明应该做什么。 In a seperate processing thread, (either just one for the system or per connection) you can then dequeue the operations and process them at a rate of 1 per .01 seconds.在单独的处理线程中(系统或每个连接只有一个),您可以将操作出列并以每 0.01 秒 1 的速率处理它们。 Just truncate the Q size to 100 (pop down to 100) before enqueing each unit of work for a given connection and voila, you discard extra work items.在为给定的连接排列每个工作单元之前,只需将 Q 大小截断为 100(向下弹出为 100),瞧,您会丢弃额外的工作项。

Note: You'll need a precise timing function to enforce 1 transaction per .01 seconds.注意:您需要一个精确的计时功能来每 0.01 秒强制执行 1 个事务。 eg:例如:

Stopwatch watch = new Stopwatch();
int nextPause = watch.Elapsed.Milliseconds + 10;
while (true)
{
    //do work (dequeue one item and process it)

    int now = watch.Elapsed.Milliseconds;
    if( now < nextPause ) {
        Thread.Sleep( nextPause - now );
    }
    nextPause = watch.Elapsed.Milliseconds + 10;
}

Note: If a transaction takes longer than 10 milliseconds (1/100th of a second), you may drop extra work items...注意:如果事务花费的时间超过 10 毫秒(1/100 秒),您可能会删除额外的工作项...

If you want the worker thread to be more "bursty" you could process multiple work items in one loop and use a longer wait time, which would require partial waits with a partial "items left" count.如果您希望工作线程更加“突发”,您可以在一个循环中处理多个工作项并使用更长的等待时间,这将需要部分等待和部分“剩余项”计数。 (also, it'd be better to use Monitor.Pulse and Montior.Wait instead of sleep...) (另外,最好使用 Monitor.Pulse 和 Montior.Wait 而不是 sleep ......)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM