You can still make this function faster and gain another second if you don't use `IEnumerable` and an enumerator but for example a `List<int>`

 public static List<int> Primes(int upperLimit)
 {
 var result = new List<int>();

 if (upperLimit < 2)
 {
 throw new ArgumentException("Upper Limit be must greater than or equal to 2.");
 }

 result.Add(2);

 if (upperLimit == 2)
 {
 return result;
 }

 // Check odd numbers for primality
 const int offset = 3;

 Func<int, int> toNumber = index => (2 * index) + offset;
 Func<int, int> toIndex = number => (int)((number - offset) * 0.5);

 var bits = new bool[toIndex(upperLimit) + 1];

 var upperSqrtIndex = toIndex((int)Math.Sqrt(upperLimit));

 for (var i = 0; i <= upperSqrtIndex; i++)
 {
 // If this bit has already been turned off, then its associated number is composite. 
 if (bits[i]) { continue;}

 var number = toNumber(i);
 
 // The instant we have a known prime, immediately yield its value.
 result.Add(number);
 
 // Any multiples of number are composite and their respective bits should be turned off.
 for (var j = toIndex(number * number); (j > i) && (j < bits.Length); j += number)
 {
 bits[j] = true;
 }
 }
 // Output remaining primes once bit array is fully resolved:
 for (var i = upperSqrtIndex + 1; i < bits.Length; i++)
 {
 if (!bits[i])
 {
 result.Add(toNumber(i));
 }
 }

 return result;
 }

Here are two profiler screenshots:

[![Enumerator][1]][1]
[![List][2]][2]


 [1]: https://i.sstatic.net/Me2xg.png
 [2]: https://i.sstatic.net/g8dZH.png