Enumerable.Empty Vs Array Initialization Syntax

The following set of code showcases two most commonly used methods to generate an empty array in C#.

var array1 = Enumerable.Empty<string>();
var array3 = new string[0];

What would be the output of the above lines of code ? One might be tempted to say both returns True. However, that isn’t quite right. The output is as follows.

Enumerable Based => True
Array Based => False

The Enumerable.Empty returns the same empty array each time you request it, while the array initialization based approach, as noticed from above code, references difference array instances. That is a subtle, but significant difference. This is evident when you lookup the source code of Enumerable.Empty method in referencesource.microsoft.com

internal class EmptyEnumerable<TElement>
{
public static readonly TElement[] Instance = new TElement[0];
}

public static IEnumerable<TResult> Empty<TResult>() 
{
return EmptyEnumerable<TResult>.Instance;
}

As you can see, the Enumerable.empty uses the array approaches internally, but it is a static instance. This kind of explains the behavior.

Let’s examine further by benchmarking the performance using Benchmark.Net. For the tests, let’s create following class.

[MemoryDiagnoser]
public class BenchMarkTest
{
        [Benchmark]
        public void MethodWithEnumerable()
        {
            var array1 = Enumerable.Empty<string>();
            var array2 = Enumerable.Empty<string>();
            var output = ReferenceEquals(array1, array2);
        }

        [Benchmark]
        public void MethodWithArray()
        {
            var array1 = new string[0];
            var array2 = new string[0];
            var output = ReferenceEquals(array1, array2);
        }
}

The output is as follows

 Method  Mean Error StdDev Gen 0 Allocated
MethodWithEnumerable    11.630 ns  0.2632 ns  0.2703 ns  –   0 B
 MethodWithArray  7.258 ns  0.0905 ns  0.0756 ns   0.0152  24 B

As you can see, the though the array approach is slightly faster, it also consumes a bit more memory (understandably). Hence, it would depend on the context of the application you are developing that would decide which approach to use.

A Closer look at DateTime Operations

There are many ways you could round off a DateTime to the nearest Hour. You could create a new Instance of DateTime, ignoring the current instance’s Minute/Seconds parts. You could also use the Add Minute method as seen in the following code.

Constructor Vs Add Method

private DateTime testData = DateTime.Now;
public DateTime FloorWithConstructor() => new DateTime(testData.Year,  testData.Month, testData.Day, testData.Hour, 0, 0);
public DateTime FloorWithAddOperator() =>  testData.AddMinute(-testData.Hour).AddSeconds(-testData.Second);

But which of the two is more efficient ? Let’s attempt to benchmark them with BenchmarkDotNet.

Method Mean Error StdDev
FloorWithConstructor 203.92 ns 4.057 ns 6.435 ns
FloorWithAddOperator 97.28 ns 2.319 ns 3.326 ns

 

Since I was under the impression that since each call to AddMinute/AddSeconds, methods would create a new instance of DateTime, I was very curious why the Add Method approach performed marginally better. But thanks to Daisy Shipton @Stackoverflow, I was able to understand why the difference in a much better way. This is also visible if you happen to refer the DateTime Source at ReferenceSource.

The Constructor : From Reference Source

public DateTime(int year, int month, int day, int hour, int minute, int second)
{
   this.dateData = (UInt64)(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second));
}
private static long DateToTicks(int year, int month, int day)
{
  if (year >= 1 && year = 1 && month = 1 && day = 0 && hour = 0 && minute =0 && second = 0? 0.5: -0.5));
  if (millis = MaxMillis)
                throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue"));
  return AddTicks(millis * TicksPerMillisecond);
}

public DateTime AddTicks(long value)
{
  long ticks = InternalTicks;
  if (value > MaxTicks - ticks || value < MinTicks - ticks)
  {
    throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_DateArithmetic"));
  }
  return new DateTime((UInt64)(ticks + value) | InternalKind);
}

 

The difference is obvious. The significant more arthimatic/conditional operations happening the particular constructor makes it rather more slower, compared to Add Methods.

The Ticks Approach

So what would be a more efficient approach ? How about if we manually work with the Ticks ? Let’s write a method and run it against the other methods.

public class DateTimeMethods
{

private DateTime testData = DateTime.Now;

[Benchmark]
public DateTime FloorWithConstructor() => new DateTime(testData.Year,  testData.Month, testData.Day, testData.Hour, 0, 0);

[Benchmark]
public DateTime FloorWithAddOperator() =>  testData.AddMinutes(-testData.Minute).AddSeconds(-testData.Second);

[Benchmark]
public DateTime FloorWithTicks()
{
  var originalTicks = testData.Ticks;
  var hoursSinceEpoch = originalTicks / TimeSpan.TicksPerHour;
  var newTicks = hoursSinceEpoch * TimeSpan.TicksPerHour;
  return new DateTime(newTicks);
}
}

 

Method Mean Error StdDev
FloorWithConstructor 206.37 ns 4.1382 ns 5.2335 ns
FloorWithAddOperator 97.81 ns 2.3568 ns 2.0892 ns
FloorWithTicks 24.47 ns 0.1492 ns 0.1246 ns

As you can see it has made a significant improvement now.No prizes for guessing how the Constructor that accepts Ticks looks like.

public DateTime(long ticks)
{
  if (ticks  MaxTicks)
    throw new ArgumentOutOfRangeException("ticks", Environment.GetResourceString("ArgumentOutOfRange_DateTimeBadTicks"));
  Contract.EndContractBlock();
  dateData = (UInt64)ticks;
}

The sample code for this demo can be found in my GitHub.