Using the comment on the original question from @TVOHM, I implemented the following code
public static int[] ReplaceUsingLinq(IEnumerable<int> arrayFromExisting, IEnumerable<int> x) { var indices = x.ToArray(); var i = 0; var newArray = arrayFromExisting.Select(val => { if (val != -1) return val; var ret = indices[i]; i++; return ret; }).ToArray(); return newArray; } public static int[] ReplceUsingForLoop(int[] arrayExisting, IEnumerable<int> x) { var arrayReplacements = x.ToArray(); var replaced = new int[arrayExisting.Length]; var replacementIndex = 0; for (var i = 0; i < arrayExisting.Length; i++) { if (arrayExisting[i] < 0) { replaced[i] = arrayReplacements[replacementIndex++]; } else { replaced[i] = arrayExisting[i]; } } return replaced; } public static unsafe int[] ReplaceUsingPointers(int[] arrayExisting, IEnumerable<int> reps) { var arrayReplacements = reps.ToArray(); int replacementsLength = arrayReplacements.Length; var replaced = new int[arrayExisting.Length]; Array.Copy(arrayExisting, replaced, arrayExisting.Length); int existingLength = replaced.Length; fixed (int* existing = replaced, replacements = arrayReplacements) { int* exist = existing; int* replace = replacements; int i = 0; int x = 0; while (i < replacementsLength && x < existingLength) { if (*exist == -1) { *exist = *replace; i++; replace++; } exist++; x++; } } return replaced; } public static int[] ReplaceUsingLoopWithMissingArray(int[] arrayExisting, IEnumerable<int> x, int[] missingIndices) { var arrayReplacements = x.ToArray(); var replaced = new int[arrayExisting.Length]; Array.Copy(arrayExisting, replaced, arrayExisting.Length); var replacementIndex = 0; foreach (var index in missingIndices) { replaced[index] = arrayReplacements[replacementIndex]; replacementIndex++; } return replaced; }
and compared this with the following code:
public void BenchmarkArrayItemReplacements() { var rand = new Random(); var arrayExisting = Enumerable.Repeat(2, 1000).ToArray(); var arrayReplacements = Enumerable.Repeat(1, 100); var toReplace = Enumerable.Range(0, 100).Select(x => rand.Next(100)).ToList(); toReplace.ForEach(x => arrayExisting[x] = -1); var misisngIndices = toReplace.ToArray(); var sw = Stopwatch.StartNew(); var result = ArrayReplacement.ReplceUsingForLoop(arrayExisting, arrayReplacements); Console.WriteLine($"for loop took {sw.ElapsedTicks}"); sw.Restart(); result = ArrayReplacement.ReplaceUsingLinq(arrayExisting, arrayReplacements); Console.WriteLine($"linq took {sw.ElapsedTicks}"); sw.Restart(); result = ArrayReplacement.ReplaceUsingLoopWithMissingArray(arrayExisting, arrayReplacements, misisngIndices); Console.WriteLine($"with missing took {sw.ElapsedTicks}"); sw.Restart(); result = ArrayReplacement.ReplaceUsingPointers(arrayExisting, arrayReplacements); Console.WriteLine($"Pointers took {sw.ElapsedTicks}"); }
This gives the results:
for loop took 848 linq took 2879 with missing took 584 Pointers took 722
So this means that knowing where we have no values โโ(where -1 s) is the key to fast.
by the way, if I loop every call to the corresponding method 10,000 times and check the time I get:
for loop took 190988 linq took 489052 with missing took 69198 Pointers took 159102
here the effect is even greater