6

Is there a more efficient way of doing the following, something just feels wrong about it? I'm looking for the most time efficient way of logging logarithmically.

    public bool Read()
    {
        long count = Interlocked.Increment(ref _count);
        switch (count)
        {
            case 1L:
            case 10L:
            case 100L:
            case 1000L:
            case 10000L:
            case 100000L:
            case 1000000L:
            case 10000000L:
            case 100000000L:
            case 1000000000L:
            case 10000000000L:
            case 100000000000L:
            case 1000000000000L:
            case 10000000000000L:
            case 100000000000000L:
            case 10000000000000000L:
            case 100000000000000000L:
            case 1000000000000000000L:
                _logger.LogFormattable(LogLevel.Debug, $"{count} Rows Read");
                break;
        }

        return _reader.Read();
    }

Update:

Here are my micro benchmark tests.

  • Method1: Übercoder's way with keep up with state
  • Method2: My way with the big switch statement
  • Method3: Markus Weninger's way with the nice Math Function

Since for me 100,000,000 records being read without logging takes around 20 minutes then an extra 4 seconds is nothing. I'm going with the beautiful Math way of doing things. Mathod3 wins in my scenario.

Run time for 100,000,000 iterations averaged over 100 attempts

Method1 Max: 00:00:00.3253789
Method1 Min: 00:00:00.2261253
Method1 Avg: 00:00:00.2417223

Method2 Max: 00:00:00.5295368
Method2 Min: 00:00:00.3618406
Method2 Avg: 00:00:00.3904475

Method3 Max: 00:00:04.0637217
Method3 Min: 00:00:03.2023237
Method3 Avg: 00:00:03.3979303
Aaron Stainback
  • 3,164
  • 1
  • 27
  • 31
  • @Dai that is on floating point, which isn't the case here. Also the question is about c#, not c++. If you have a better, duplicate, let me know. – Patrick Hofman Feb 12 '16 at 22:24
  • 1
    Using a `switch` statement like you do really is the fastest way to do this as it will be compiled into a series of processor-level hashtable instructions which are lightning fast. While the code is "ugly" (well, I think it's kind-of beautiful) I think you can improve it by moving it to a separate function (`IsExactlyPowerOf10(Int64 n)") and calling that from your `Read` function. – Dai Feb 12 '16 at 23:59
  • Thanks for the nice benchmarking of all the three methods :) – Markus Weninger Feb 17 '16 at 20:57

2 Answers2

8

If performance is not a big problem, I would use the following

if(Math.Log10(count) % 1 == 0)
  _logger.LogFormattable(LogLevel.Debug, $"{count} Rows Read");

This question states the following:

For floating point numbers, n % 1 == 0 is typically the way to check if there is anything past the decimal point.


Edit: To complete my answer, it is also possible to keep track of the next logging value, as @Übercoder posted in his answer.

long nextLoggingValueForLogX = 1;
if (count == nextLoggingValueForLogX )
{
   nextLoggingValueForLogX *= 10; // Increase it by your needs, e.g., logarithmically by multiplying with 10
   _logger.LogFormattable(LogLevel.Debug, $"{count} Rows Read");
}

Yet this method will come up with a new variable for every log that shouldn't be executed every time. This will introduce extra code and also extra work if it has to be made thread-safe.

Community
  • 1
  • 1
Markus Weninger
  • 8,844
  • 4
  • 47
  • 112
  • I will do a performance test of mine vs yours and see what comes out faster, I really hope it's your because it's much more beautiful. Even if it's a little slower I will probably still use it. Let me do some performance tests to see if I can mark this as the answer. – Aaron Stainback Feb 12 '16 at 22:43
  • Yeah, I'm also interested in this. I hope it works out for you. – Markus Weninger Feb 12 '16 at 22:44
3
static long logTrigger = 1;


if (count == logTrigger)
{
   logTrigger *= 10;
   // do your logging
}
Jens Meinecke
  • 2,784
  • 15
  • 19