From 91f543aa84e73c4fc8a7dfa7847a28a7ff5c22bb Mon Sep 17 00:00:00 2001 From: inga-lovinde <52715130+inga-lovinde@users.noreply.github.com> Date: Mon, 13 Mar 2017 00:39:23 +0300 Subject: [PATCH] Microoptimization: performance-critical methods are made static --- WhiteRabbit/Program.cs | 1 + WhiteRabbit/StringsProcessor.cs | 6 ++-- WhiteRabbit/VectorsProcessor.cs | 49 +++++++++++---------------------- 3 files changed, 20 insertions(+), 36 deletions(-) diff --git a/WhiteRabbit/Program.cs b/WhiteRabbit/Program.cs index 8be319d..47ea3b9 100644 --- a/WhiteRabbit/Program.cs +++ b/WhiteRabbit/Program.cs @@ -39,6 +39,7 @@ Console.WriteLine($"Initialization complete; time from start: {stopwatch.Elapsed}"); #if DEBUG + // it makes the program slow (as all anagrams are generated twice), but this code is only run in a debug mode var totalAnagramsCount = processor.GeneratePhrases().Count(); Console.WriteLine($"Total anagrams count: {totalAnagramsCount}; time from start: {stopwatch.Elapsed}"); #endif diff --git a/WhiteRabbit/StringsProcessor.cs b/WhiteRabbit/StringsProcessor.cs index fe248b3..8b7f06f 100644 --- a/WhiteRabbit/StringsProcessor.cs +++ b/WhiteRabbit/StringsProcessor.cs @@ -46,14 +46,14 @@ // converting sequences of vectors to the sequences of words... var anagramsWords = sums .Select(sum => ImmutableStack.Create(sum.Select(vector => this.VectorsToWords[vector]).ToArray())) - .SelectMany(this.Flatten) + .SelectMany(Flatten) .Select(stack => stack.ToArray()); return anagramsWords.Select(WordsToPhrase); } // Converts e.g. pair of variants [[a, b, c], [d, e]] into all possible pairs: [[a, d], [a, e], [b, d], [b, e], [c, d], [c, e]] - private IEnumerable> Flatten(ImmutableStack phrase) + private static IEnumerable> Flatten(ImmutableStack phrase) { if (phrase.IsEmpty) { @@ -62,7 +62,7 @@ T[] wordVariants; var newStack = phrase.Pop(out wordVariants); - return this.Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word))); + return Flatten(newStack).SelectMany(remainder => wordVariants.Select(word => remainder.Push(word))); } private byte[] WordsToPhrase(byte[][] words) diff --git a/WhiteRabbit/VectorsProcessor.cs b/WhiteRabbit/VectorsProcessor.cs index 16229f2..b3cb5ab 100644 --- a/WhiteRabbit/VectorsProcessor.cs +++ b/WhiteRabbit/VectorsProcessor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; - using System.Diagnostics; using System.Linq; using System.Numerics; @@ -45,10 +44,10 @@ // Produces all sequences of vectors with the target sum public ParallelQuery[]> GenerateSequences() { - return this.GenerateUnorderedSequences(this.Target, GetVectorNorm(this.Target, this.Target), this.MaxVectorsCount, 0) + return GenerateUnorderedSequences(this.Target, GetVectorNorm(this.Target, this.Target), this.MaxVectorsCount, this.Dictionary, 0) .AsParallel() .Select(Enumerable.ToArray) - .SelectMany(this.GeneratePermutations); + .SelectMany(GeneratePermutations); } // We want words with more letters (and among these, words with more "rare" letters) to appear first, to reduce the searching time somewhat. @@ -77,21 +76,11 @@ .ToArray(); } - [Conditional("DEBUG")] - private void DebugState(int allowedRemainingWords, Vector currentVector) - { - this.Iterations++; - if (this.Iterations % 1000000 == 0) - { - Console.WriteLine($"Iteration #{this.Iterations}: {allowedRemainingWords}, {this.VectorToString(currentVector)}"); - } - } - // This method takes most of the time, so everything related to it must be optimized. // In every sequence, next vector always goes after the previous one from dictionary. // E.g. if dictionary is [x, y, z], then only [x, y] sequence could be generated, and [y, x] will never be generated. // That way, the complexity of search goes down by a factor of MaxVectorsCount! (as if [x, y] does not add up to a required target, there is no point in checking [y, x]) - private IEnumerable>> GenerateUnorderedSequences(Vector remainder, int remainderNorm, int allowedRemainingWords, int currentDictionaryPosition) + private static IEnumerable>> GenerateUnorderedSequences(Vector remainder, int remainderNorm, int allowedRemainingWords, ImmutableArray dictionary, int currentDictionaryPosition) { if (allowedRemainingWords > 1) { @@ -100,12 +89,9 @@ // we need the largest remaining word to have a norm of at least 3 var requiredRemainderPerWord = (remainderNorm + allowedRemainingWords - 1) / allowedRemainingWords; - for (var i = FindFirstWithNormLessOrEqual(remainderNorm, currentDictionaryPosition); i < this.Dictionary.Length; i++) + for (var i = FindFirstWithNormLessOrEqual(remainderNorm, dictionary, currentDictionaryPosition); i < dictionary.Length; i++) { - var currentVectorInfo = this.Dictionary[i]; - - this.DebugState(allowedRemainingWords, currentVectorInfo.Vector); - + var currentVectorInfo = dictionary[i]; if (currentVectorInfo.Vector == remainder) { yield return ImmutableStack.Create(currentVectorInfo.Vector); @@ -118,7 +104,7 @@ { var newRemainder = remainder - currentVectorInfo.Vector; var newRemainderNorm = remainderNorm - currentVectorInfo.Norm; - foreach (var result in this.GenerateUnorderedSequences(newRemainder, newRemainderNorm, newAllowedRemainingWords, i)) + foreach (var result in GenerateUnorderedSequences(newRemainder, newRemainderNorm, newAllowedRemainingWords, dictionary, i)) { yield return result.Push(currentVectorInfo.Vector); } @@ -127,12 +113,9 @@ } else { - for (var i = FindFirstWithNormLessOrEqual(remainderNorm, currentDictionaryPosition); i < this.Dictionary.Length; i++) + for (var i = FindFirstWithNormLessOrEqual(remainderNorm, dictionary, currentDictionaryPosition); i < dictionary.Length; i++) { - var currentVectorInfo = this.Dictionary[i]; - - this.DebugState(allowedRemainingWords, currentVectorInfo.Vector); - + var currentVectorInfo = dictionary[i]; if (currentVectorInfo.Vector == remainder) { yield return ImmutableStack.Create(currentVectorInfo.Vector); @@ -146,19 +129,19 @@ } // BCL BinarySearch would find any vector with required norm, not the first one; or would find nothing if there is no such vector - private int FindFirstWithNormLessOrEqual(int expectedNorm, int offset) + private static int FindFirstWithNormLessOrEqual(int expectedNorm, ImmutableArray dictionary, int offset) { var start = offset; - var end = this.Dictionary.Length - 1; + var end = dictionary.Length - 1; - if (this.Dictionary[start].Norm <= expectedNorm) + if (dictionary[start].Norm <= expectedNorm) { return start; } - if (this.Dictionary[end].Norm > expectedNorm) + if (dictionary[end].Norm > expectedNorm) { - return this.Dictionary.Length; + return dictionary.Length; } // Norm for start is always greater than expected norm, or start is the required position; norm for end is always less than or equal to expected norm @@ -166,8 +149,8 @@ while (start < end) { var middle = (start + end) / 2; - var newNorm = this.Dictionary[middle].Norm; - if (this.Dictionary[middle].Norm <= expectedNorm) + var newNorm = dictionary[middle].Norm; + if (dictionary[middle].Norm <= expectedNorm) { end = middle; } @@ -180,7 +163,7 @@ return start; } - private IEnumerable GeneratePermutations(T[] original) + private static IEnumerable GeneratePermutations(T[] original) { foreach (var permutation in PrecomputedPermutationsGenerator.HamiltonianPermutations(original.Length)) {