5
\$\begingroup\$

I need to limit method call to 80 times per second (for each connectionId).

My initial version:

private const int MAX_TRANSACTIONS_PER_PERIOD = 80;
private const int PERIOD_IN_MS = 1000;
private Queue<DateTime>[] times = new Queue<DateTime>[10]
{
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100),
    new Queue<DateTime>(100)
};

private int ExecTransWithStats(int connection_id, string name, string parameters_string, byte[] bytes)
{
    DateTime nowTime = DateTime.Now;
    while (times[connection_id].Count > 0 && (nowTime - times[connection_id].Peek()).TotalMilliseconds >= PERIOD_IN_MS)
    {
        times[connection_id].Dequeue();
    }
    if (times[connection_id].Count >= MAX_TRANSACTIONS_PER_PERIOD)
    {
        Thread.Sleep(PERIOD_IN_MS - (int) (nowTime - times[connection_id].Dequeue()).TotalMilliseconds);
    }
    times[connection_id].Enqueue(DateTime.Now);

    return ExecTrans(connection_id, name, parameters_string, bytes);
}

I've decided to replace DateTime with Stopwatch just because I don't need to store entire Date. I only need to store how many milliseconds elapsed:

private const int MAX_TRANSACTIONS_PER_PERIOD = 80;
private const int PERIOD_IN_MS = 1000;
private Queue<long>[] times = new Queue<long>[10]
{
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100),
    new Queue<long>(100)
};

private Stopwatch workingTime = Stopwatch.StartNew();

private int ExecTransWithStats(int connection_id, string name, string parameters_string, byte[] bytes)
{
    long nowTime = workingTime.ElapsedMilliseconds;
    while (times[connection_id].Count > 0 && (nowTime - times[connection_id].Peek()) >= PERIOD_IN_MS)
    {
        times[connection_id].Dequeue();
    }
    if (times[connection_id].Count >= MAX_TRANSACTIONS_PER_PERIOD)
    {
        Thread.Sleep(PERIOD_IN_MS - (int) (nowTime - times[connection_id].Dequeue()));
    }
    times[connection_id].Enqueue(workingTime.ElapsedMilliseconds);

    return ExecTrans(connection_id, name, parameters_string, bytes);
}

Am I correct that version with Stopwatch is better? It has much better precision, but I'm ok with having precision of DateTime (10-15 ms). Do I pay for extra precision (which I don't actually need)?

\$\endgroup\$

1 Answer 1

6
\$\begingroup\$

StopWatch is definitely better for timing stuff, but the semantics of the two versions are different. With DateTime you have this:

private int ExecTransWithStats(int connection_id, string name, string parameters_string, byte[] bytes)
{
    DateTime nowTime = DateTime.Now;
   ...

and with StopWatch you have that:

private Stopwatch workingTime = Stopwatch.StartNew();
private int ExecTransWithStats(int connection_id, string name, string parameters_string, byte[] bytes)
{
    long nowTime = workingTime.ElapsedMilliseconds;
  ...

Bug?

The stopwatch starts as soon as the instance is created, it looks like you're misusing the stopwatch.

long nowTime = workingTime.ElapsedMilliseconds;

That's not how a stopwatch works. That will give you the number of milliseconds elapsed between the instantiation of your class and the moment ExecTransWithStats gets called. I doubt this is what you are expecting.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.