title: I need the fastest way to process math on numerical array data
body: <p>I apologize if this is in the incorrect forum. Despite finding a lot of Array manipulation on this site, most of these are averaging/summing... the array of numerics as a set using LINQ, which processes well for all values in an array. But I need to process each index over multiple arrays (of the same size).</p>
  <p>My routine receives array data from devices, typically <code>double[512]</code> or <code>ushort[512]</code>; A single device itself will always have the same size of Array data, but the array sizes can range from 256 to 2048 depending on the device. I need to hold <code>CountToAverage</code> quantity of the arrays to average. Each time an array is received, it must push and pop from the queue to ensure that the number of arrays in the average process is consistent (this part of the process is fixed in the <code>Setup()</code> for this benchmark testing. For comparison purposes, the benchmark results are shown after the code.</p>
  <ol>
  <li><p>What I am looking for is the fastest most efficient way to average the values of each index of all the arrays, and return a new array (of the same size) where each index is averaged from the set of arrays. The count of arrays to be averaged can range from 3 to 25 (the code below sets benchmark param to 10). I have 2 different averaging methods in the test, the 2nd is significantly faster, 6-7 times faster than the first. My first question is; Is there any way to achieve this faster, that can be done at O(1) or O(log n) time complexity?</p>
  </li>
  <li><p>Secondarily, I am using a Queue (which may be changed to <code>ConcurrentQueue</code> for implementation) as a holder for the arrays to be processed. My primary reasoning for using a queue is because I can guarantee FIFO processing of the feed of arrays which is critical. Also, I can process against the values in the Queue through a <code>foreach</code> loop (just like a <code>List</code>) without having to dequeue until I am ready. I would be interested if anyone knows whether this is performance hindering as I haven't benchmarked it. Keep in mind it must be thread-safe. If you have an alternative way to process multiple sets of array data in a thread-safe manner I am &quot;all ears&quot;.</p>
  </li>
  </ol>
  <p>The reason for the performance requirement is this is not the only process that is happening, I have multiple devices that are sending array results &quot;streamed&quot; at an approximate rate of 1 every 1-5 milliseconds, for each device coming from different threads/processes/connections, that still has several other much more intensive algorithms to process through, so this cannot be a bottleneck.</p>
  <p>Any insights on optimizations and performance are appreciated.</p>
  <pre><code>using System;
  using System.Collections.Generic;
  using BenchmarkDotNet.Attributes;
  using BenchmarkDotNet.Jobs;
  using BenchmarkDotNet.Running;
  using Microsoft.Diagnostics.Tracing.Parsers.MicrosoftAntimalwareEngine;
  
  namespace ArrayAverage
  {
      public class ArrayAverage
      {
          [Params(10)]
          public int CountToAverage;
  
          [Params(512, 2048)]
          public int PixelSize;
  
          static Queue&lt;double[]&gt; calcRepo = new Queue&lt;double[]&gt;();
          static List&lt;double[]&gt; spectra = new();
          
          [Benchmark]
          public double[] CalculateIndexAverages()
          {
              // This is too slow
              var avg = new double[PixelSize];
              for (int i = 0; i &lt; PixelSize; i++)
              {
                  foreach (var arrayData in calcRepo)
                  {
                      avg[i] += arrayData[i];
                  }
                  avg[i] /= calcRepo.Count;
              }
              return avg;
          }
          
          [Benchmark]
          public double[] CalculateIndexAverages2()
          {
              // this is faster, but is it the fastest?
              var sum = new double[PixelSize];
              int cnt = calcRepo.Count;
              foreach (var arrayData in calcRepo)
              {
                  for (int i = 0; i &lt; PixelSize; i++)
                  {
                      sum[i] += arrayData[i];
                  }
              }
  
              var avg = new double[PixelSize];
              for (int i = 0; i &lt; PixelSize; i++)
              {
                  avg[i] = sum[i] / cnt;
              }
  
              return avg;
          }
          
          [GlobalSetup]
          public void Setup()
          {
              // Just generating some data as simple Triangular curve simulating a range of spectra
              for (double offset = 0; offset &lt; CountToAverage; offset++)
              {
                  var values = new double[PixelSize];
                  var decrement = 0;
                  for (int i = 0; i &lt; PixelSize; i++)
                  {
                      if (i &gt; (PixelSize / 2))
                          decrement--;
                      values[i] = (offset / 7) + i + (decrement * 2);
                  }
                  calcRepo.Enqueue(values);
              }
          }        
      }
      
      public class App
      {
          public static void Main()
          {
              BenchmarkRunner.Run&lt;ArrayAverage&gt;();
          }
      }
  }
  
  </code></pre>
  <p>Benchmark results:</p>
  <pre><code>
  BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1348 (21H1/May2021Update)
  Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
  .NET SDK=6.0.100-preview.7.21379.14
    [Host]     : .NET 5.0.12 (5.0.1221.52207), X64 RyuJIT  [AttachedDebugger]
    DefaultJob : .NET 5.0.12 (5.0.1221.52207), X64 RyuJIT
  </code></pre>
  <div class="s-table-container">
  <table class="s-table">
  <thead>
  <tr>
  <th>Method</th>
  <th>Arrays To Average</th>
  <th>Array Size</th>
  <th style="text-align: right;">Mean</th>
  <th style="text-align: right;">Error</th>
  <th style="text-align: right;">StdDev</th>
  </tr>
  </thead>
  <tbody>
  <tr>
  <td><strong>CalculateIndexAverages</strong></td>
  <td><strong>10</strong></td>
  <td><strong>512</strong></td>
  <td style="text-align: right;"><strong>32.164 μs</strong></td>
  <td style="text-align: right;"><strong>0.5485 μs</strong></td>
  <td style="text-align: right;"><strong>0.5130 μs</strong></td>
  </tr>
  <tr>
  <td>CalculateIndexAverages2</td>
  <td>10</td>
  <td>512</td>
  <td style="text-align: right;">5.792 μs</td>
  <td style="text-align: right;">0.1135 μs</td>
  <td style="text-align: right;">0.2241 μs</td>
  </tr>
  <tr>
  <td><strong>CalculateIndexAverages</strong></td>
  <td><strong>10</strong></td>
  <td><strong>2048</strong></td>
  <td style="text-align: right;"><strong>123.628 μs</strong></td>
  <td style="text-align: right;"><strong>2.3394 μs</strong></td>
  <td style="text-align: right;"><strong>1.9535 μs</strong></td>
  </tr>
  <tr>
  <td>CalculateIndexAverages2</td>
  <td>10</td>
  <td>2048</td>
  <td style="text-align: right;">22.311 μs</td>
  <td style="text-align: right;">0.4366 μs</td>
  <td style="text-align: right;">0.8093 μs</td>
  </tr>
  </tbody>
  </table>
  </div>