Microoptimization: performance-critical methods are made static

feature-optimized-md5
Inga 🏳‍🌈 7 years ago
parent fc5164fde2
commit 91f543aa84
  1. 1
      WhiteRabbit/Program.cs
  2. 6
      WhiteRabbit/StringsProcessor.cs
  3. 49
      WhiteRabbit/VectorsProcessor.cs

@ -39,6 +39,7 @@
Console.WriteLine($"Initialization complete; time from start: {stopwatch.Elapsed}"); Console.WriteLine($"Initialization complete; time from start: {stopwatch.Elapsed}");
#if DEBUG #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(); var totalAnagramsCount = processor.GeneratePhrases().Count();
Console.WriteLine($"Total anagrams count: {totalAnagramsCount}; time from start: {stopwatch.Elapsed}"); Console.WriteLine($"Total anagrams count: {totalAnagramsCount}; time from start: {stopwatch.Elapsed}");
#endif #endif

@ -46,14 +46,14 @@
// converting sequences of vectors to the sequences of words... // converting sequences of vectors to the sequences of words...
var anagramsWords = sums var anagramsWords = sums
.Select(sum => ImmutableStack.Create(sum.Select(vector => this.VectorsToWords[vector]).ToArray())) .Select(sum => ImmutableStack.Create(sum.Select(vector => this.VectorsToWords[vector]).ToArray()))
.SelectMany(this.Flatten) .SelectMany(Flatten)
.Select(stack => stack.ToArray()); .Select(stack => stack.ToArray());
return anagramsWords.Select(WordsToPhrase); 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]] // 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<ImmutableStack<T>> Flatten<T>(ImmutableStack<T[]> phrase) private static IEnumerable<ImmutableStack<T>> Flatten<T>(ImmutableStack<T[]> phrase)
{ {
if (phrase.IsEmpty) if (phrase.IsEmpty)
{ {
@ -62,7 +62,7 @@
T[] wordVariants; T[] wordVariants;
var newStack = phrase.Pop(out 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) private byte[] WordsToPhrase(byte[][] words)

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
@ -45,10 +44,10 @@
// Produces all sequences of vectors with the target sum // Produces all sequences of vectors with the target sum
public ParallelQuery<Vector<byte>[]> GenerateSequences() public ParallelQuery<Vector<byte>[]> 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() .AsParallel()
.Select(Enumerable.ToArray) .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. // 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(); .ToArray();
} }
[Conditional("DEBUG")]
private void DebugState(int allowedRemainingWords, Vector<byte> 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. // 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. // 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. // 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]) // 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<ImmutableStack<Vector<byte>>> GenerateUnorderedSequences(Vector<byte> remainder, int remainderNorm, int allowedRemainingWords, int currentDictionaryPosition) private static IEnumerable<ImmutableStack<Vector<byte>>> GenerateUnorderedSequences(Vector<byte> remainder, int remainderNorm, int allowedRemainingWords, ImmutableArray<VectorInfo> dictionary, int currentDictionaryPosition)
{ {
if (allowedRemainingWords > 1) if (allowedRemainingWords > 1)
{ {
@ -100,12 +89,9 @@
// we need the largest remaining word to have a norm of at least 3 // we need the largest remaining word to have a norm of at least 3
var requiredRemainderPerWord = (remainderNorm + allowedRemainingWords - 1) / allowedRemainingWords; 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]; var currentVectorInfo = dictionary[i];
this.DebugState(allowedRemainingWords, currentVectorInfo.Vector);
if (currentVectorInfo.Vector == remainder) if (currentVectorInfo.Vector == remainder)
{ {
yield return ImmutableStack.Create(currentVectorInfo.Vector); yield return ImmutableStack.Create(currentVectorInfo.Vector);
@ -118,7 +104,7 @@
{ {
var newRemainder = remainder - currentVectorInfo.Vector; var newRemainder = remainder - currentVectorInfo.Vector;
var newRemainderNorm = remainderNorm - currentVectorInfo.Norm; 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); yield return result.Push(currentVectorInfo.Vector);
} }
@ -127,12 +113,9 @@
} }
else 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]; var currentVectorInfo = dictionary[i];
this.DebugState(allowedRemainingWords, currentVectorInfo.Vector);
if (currentVectorInfo.Vector == remainder) if (currentVectorInfo.Vector == remainder)
{ {
yield return ImmutableStack.Create(currentVectorInfo.Vector); 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 // 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<VectorInfo> dictionary, int offset)
{ {
var start = 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; 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 // 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) while (start < end)
{ {
var middle = (start + end) / 2; var middle = (start + end) / 2;
var newNorm = this.Dictionary[middle].Norm; var newNorm = dictionary[middle].Norm;
if (this.Dictionary[middle].Norm <= expectedNorm) if (dictionary[middle].Norm <= expectedNorm)
{ {
end = middle; end = middle;
} }
@ -180,7 +163,7 @@
return start; return start;
} }
private IEnumerable<T[]> GeneratePermutations<T>(T[] original) private static IEnumerable<T[]> GeneratePermutations<T>(T[] original)
{ {
foreach (var permutation in PrecomputedPermutationsGenerator.HamiltonianPermutations(original.Length)) foreach (var permutation in PrecomputedPermutationsGenerator.HamiltonianPermutations(original.Length))
{ {

Loading…
Cancel
Save