diff --git a/README.md b/README.md index 4c214d0..017cd67 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,10 @@ Number of words|Time to check all anagrams no longer than that|Time to solve "ea ---------------|----------------------------------------------|-------------------------|-----------------------------------|-------------------------|--------------------------------------------- 3|0.04s||||4560 4|0.45s|||0.08s|7,431,984 -5|18s|0.25s|0.07s|0.5s|1,347,437,484 -6|9.5 minutes|1.7s|0.3s|4.3s|58,405,904,844 -7|3.2 hours|10s|1.3s|28s|1,070,307,744,114 -8|49 hours|37s|3.8s|1.9 minutes|10,893,594,396,594 +5|12s|0.16s|0.06s|0.32s|1,347,437,484 +6|6.5 minutes|1.25s|0.25s|3s|58,405,904,844 +7|3.2 hours|7s|0.9s|19s|1,070,307,744,114 +8|49 hours|25s|2.6s|80s|10,893,594,396,594 9||2.5 minutes|13s|9.5 minutes|70,596,864,409,954 10||5 minutes|21s|17.5 minutes|314,972,701,475,754 diff --git a/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.cpp b/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.cpp index 9761b74..9e11414 100644 --- a/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.cpp +++ b/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.cpp @@ -27,7 +27,7 @@ void WhiteRabbitUnmanagedBridge::MD5Unmanaged::ComputeMD5(unsigned __int32 * inp #endif } -void WhiteRabbitUnmanagedBridge::MD5Unmanaged::FillPhraseSet(__int64* bufferPointer, __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int permutationOffset, int numberOfCharacters, int numberOfWords) +void WhiteRabbitUnmanagedBridge::MD5Unmanaged::FillPhraseSet(__int64* bufferPointer, __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int numberOfCharacters, int numberOfWords) { - fillPhraseSet(bufferPointer, (unsigned __int64*)allWordsPointer, wordIndexes, permutationsPointer, permutationOffset, numberOfCharacters, numberOfWords); + fillPhraseSet(bufferPointer, (unsigned __int64*)allWordsPointer, wordIndexes, permutationsPointer, numberOfCharacters, numberOfWords); } diff --git a/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.h b/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.h index afc6add..41468b5 100644 --- a/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.h +++ b/dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.h @@ -13,6 +13,6 @@ namespace WhiteRabbitUnmanagedBridge { public: literal int PhrasesPerSet = PHRASES_PER_SET; static void ComputeMD5(unsigned int* input); - static void FillPhraseSet(__int64* bufferPointer, __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int permutationOffset, int numberOfCharacters, int numberOfWords); + static void FillPhraseSet(__int64* bufferPointer, __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int numberOfCharacters, int numberOfWords); }; } diff --git a/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.cpp b/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.cpp index 88e41aa..767d45a 100644 --- a/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.cpp +++ b/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.cpp @@ -25,9 +25,9 @@ macro(15); #define INIT_WORD(phraseNumber) \ - auto permutation = permutationsPointer[permutationOffset + phraseNumber]; \ + auto permutation = permutationsPointer[phraseNumber]; \ unsigned __int64 cumulativeWordOffset = 0; \ - auto phrase = avx2buffer[phraseNumber]; + auto phrase = _mm256_set1_epi32(0); #define PROCESS_WORD(phraseNumber, wordNumber) \ { \ @@ -118,7 +118,7 @@ #define REPEAT_WORDS10(phraseNumber) REPEAT_WORDS(phraseNumber, REPEAT_WORDS_SIMPLE10) -void fillPhraseSet(__int64* bufferPointer, unsigned __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int permutationOffset, int numberOfCharacters, int numberOfWords) +void fillPhraseSet(__int64* bufferPointer, unsigned __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int numberOfCharacters, int numberOfWords) { auto avx2buffer = (__m256i*)bufferPointer; diff --git a/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.h b/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.h index 8417b2b..d13d516 100644 --- a/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.h +++ b/dotnet/WhiteRabbit.UnmanagedBridge/phraseset.h @@ -1,3 +1,3 @@ #pragma once -void fillPhraseSet(__int64* bufferPointer, unsigned __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int permutationOffset, int numberOfCharacters, int numberOfWords); +void fillPhraseSet(__int64* bufferPointer, unsigned __int64* allWordsPointer, __int32* wordIndexes, unsigned __int64* permutationsPointer, int numberOfCharacters, int numberOfWords); diff --git a/dotnet/WhiteRabbit/MD5Digest.cs b/dotnet/WhiteRabbit/MD5Digest.cs deleted file mode 100644 index 6a8fb6d..0000000 --- a/dotnet/WhiteRabbit/MD5Digest.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace WhiteRabbit -{ - using System.Runtime.CompilerServices; - using WhiteRabbitUnmanagedBridge; - - internal static class MD5Digest - { - // It only returns first component of MD5 hash - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Compute(PhraseSet input) - { - fixed (uint* inputBuffer = input.Buffer) - { - MD5Unmanaged.ComputeMD5(inputBuffer); - } - } - } -} diff --git a/dotnet/WhiteRabbit/PhraseSet.cs b/dotnet/WhiteRabbit/PhraseSet.cs index 6662b27..b3dc8e2 100644 --- a/dotnet/WhiteRabbit/PhraseSet.cs +++ b/dotnet/WhiteRabbit/PhraseSet.cs @@ -2,17 +2,24 @@ { using System.Diagnostics; using System.Runtime.CompilerServices; + using WhiteRabbitUnmanagedBridge; // Anagram representation optimized for MD5 internal struct PhraseSet { - public uint[] Buffer; + private uint[] Buffer; - public unsafe PhraseSet(Word[] allWords, int[] wordIndexes, ulong[] permutations, int permutationOffset, int numberOfCharacters) + public int Length; + + public PhraseSet(int length) { - Debug.Assert(numberOfCharacters + wordIndexes.Length - 1 < 27); + this.Length = length; + this.Buffer = new uint[8 * length]; + } - this.Buffer = new uint[8 * Constants.PhrasesPerSet]; + public unsafe void FillPhraseSet(Word[] allWords, int[] wordIndexes, ulong[] permutations, int permutationOffset, int numberOfCharacters) + { + Debug.Assert(numberOfCharacters + wordIndexes.Length - 1 < 27); fixed (uint* bufferPointer = this.Buffer) { @@ -22,13 +29,27 @@ { fixed (Word* allWordsPointer = allWords) { - WhiteRabbitUnmanagedBridge.MD5Unmanaged.FillPhraseSet((long*)bufferPointer, (long*)allWordsPointer, wordIndexesPointer, permutationsPointer, permutationOffset, numberOfCharacters, wordIndexes.Length); + MD5Unmanaged.FillPhraseSet( + (long*)bufferPointer, + (long*)allWordsPointer, + wordIndexesPointer, + permutationsPointer + permutationOffset, + numberOfCharacters, + wordIndexes.Length); } } } } } + public unsafe void ComputeMD5() + { + fixed (uint* inputBuffer = this.Buffer) + { + MD5Unmanaged.ComputeMD5(inputBuffer); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint GetMD5(int number) { diff --git a/dotnet/WhiteRabbit/Program.cs b/dotnet/WhiteRabbit/Program.cs index c28cf37..750b292 100644 --- a/dotnet/WhiteRabbit/Program.cs +++ b/dotnet/WhiteRabbit/Program.cs @@ -80,14 +80,13 @@ processor.CheckPhrases(phraseSet => { - MD5Digest.Compute(phraseSet); - for (var i = 0; i < Constants.PhrasesPerSet; i++) + phraseSet.ComputeMD5(); + for (var i = 0; i < phraseSet.Length; i++) { Debug.Assert( sourceChars == ToOrderedChars(ToString(phraseSet, i)), $"StringsProcessor produced incorrect anagram: {ToString(phraseSet, i)}"); - if (Vector.EqualsAny(expectedHashesFirstComponents, new Vector(hashesFirstComponents[i]))) if (Vector.EqualsAny(expectedHashesFirstComponents, new Vector(phraseSet.GetMD5(i)))) { var phrase = ToString(phraseSet, i); diff --git a/dotnet/WhiteRabbit/StringsProcessor.cs b/dotnet/WhiteRabbit/StringsProcessor.cs index 69777f5..0ddbb3a 100644 --- a/dotnet/WhiteRabbit/StringsProcessor.cs +++ b/dotnet/WhiteRabbit/StringsProcessor.cs @@ -68,12 +68,17 @@ // converting sequences of vectors to the sequences of words... Parallel.ForEach(sums, new ParallelOptions { MaxDegreeOfParallelism = Constants.NumberOfThreads }, sum => { + var phraseSet = new PhraseSet(Constants.PhrasesPerSet); var filter = ComputeFilter(sum); var wordsVariants = this.ConvertVectorsToWordIndexes(sum); foreach (var wordsArray in Flattener.Flatten(wordsVariants)) { - foreach (var phraseSet in this.ConvertWordsToPhrases(wordsArray, filter)) + //Console.WriteLine(new string(wordsArray.SelectMany(wordIndex => this.AllWords[wordIndex].Original).Select(b => (char)b).ToArray())); + + var permutations = PrecomputedPermutationsGenerator.HamiltonianPermutations(wordsArray.Length, filter); + for (var i = 0; i < permutations.Length; i += Constants.PhrasesPerSet) { + phraseSet.FillPhraseSet(this.AllWords, wordsArray, permutations, i, this.NumberOfCharacters); action(phraseSet); } } @@ -128,15 +133,5 @@ return result; } - - private IEnumerable ConvertWordsToPhrases(int[] wordIndexes, uint filter) - { - var permutations = PrecomputedPermutationsGenerator.HamiltonianPermutations(wordIndexes.Length, filter); - var permutationsLength = permutations.Length; - for (var i = 0; i < permutationsLength; i += Constants.PhrasesPerSet) - { - yield return new PhraseSet(this.AllWords, wordIndexes, permutations, i, this.NumberOfCharacters); - } - } } } diff --git a/dotnet/WhiteRabbit/Word.cs b/dotnet/WhiteRabbit/Word.cs index 68d5e98..8ecc31f 100644 --- a/dotnet/WhiteRabbit/Word.cs +++ b/dotnet/WhiteRabbit/Word.cs @@ -30,6 +30,24 @@ } } + public unsafe byte[] Original + { + get + { + fixed (long* buffersPointer = this.Buffers) + { + var length = buffersPointer[127] / 4; + var result = new byte[length]; + for (var i = 0; i < length; i++) + { + result[i] = ((byte*)buffersPointer)[i]; + } + + return result; + } + } + } + private static Word Empty { get; } = new Word(); } }