Optimized memory allocations (MD5 is stored inside a PhraseSet)

master
Inga 🏳‍🌈 7 years ago
parent 05040b030f
commit 16bc5f2c98
  1. 10
      README.md
  2. 16
      dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.cpp
  3. 2
      dotnet/WhiteRabbit.UnmanagedBridge/WhiteRabbit.UnmanagedBridge.h
  4. 24
      dotnet/WhiteRabbit.UnmanagedBridge/md5.cpp
  5. 2
      dotnet/WhiteRabbit.UnmanagedBridge/md5.h
  6. 12
      dotnet/WhiteRabbit/MD5Digest.cs
  7. 28
      dotnet/WhiteRabbit/PhraseSet.cs
  8. 5
      dotnet/WhiteRabbit/Program.cs

@ -46,11 +46,11 @@ Multi-threaded performance with RyuJIT (.NET 4.6, 64-bit system) on i5-6500 is a
Number of words|Time to check all anagrams no longer than that|Time to solve "easy" hash|Time to solve "more difficult" hash|Time to solve "hard" hash|Number of unique anagrams no longer than that Number of words|Time to check all anagrams no longer than that|Time to solve "easy" hash|Time to solve "more difficult" hash|Time to solve "hard" hash|Number of unique anagrams no longer than that
---------------|----------------------------------------------|-------------------------|-----------------------------------|-------------------------|--------------------------------------------- ---------------|----------------------------------------------|-------------------------|-----------------------------------|-------------------------|---------------------------------------------
3|0.04s||||4560 3|0.04s||||4560
4|0.65s|||0.1s|7,431,984 4|0.45s|||0.08s|7,431,984
5|19s|0.3s|0.1s|0.5s|1,347,437,484 5|18s|0.25s|0.07s|0.5s|1,347,437,484
6|10.5 minutes|1.9s|0.35s|4.7s|58,405,904,844 6|9.5 minutes|1.7s|0.3s|4.3s|58,405,904,844
7|3.2 hours|11s|1.4s|31.5s|1,070,307,744,114 7|3.2 hours|10s|1.3s|28s|1,070,307,744,114
8|49 hours|40s|4.1s|2.1 minutes|10,893,594,396,594 8|49 hours|37s|3.8s|1.9 minutes|10,893,594,396,594
9||2.5 minutes|13s|9.5 minutes|70,596,864,409,954 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 10||5 minutes|21s|17.5 minutes|314,972,701,475,754

@ -6,23 +6,23 @@
#include "md5.h" #include "md5.h"
#include "phraseset.h" #include "phraseset.h"
void WhiteRabbitUnmanagedBridge::MD5Unmanaged::ComputeMD5(unsigned __int32 * input, unsigned __int32 * output) void WhiteRabbitUnmanagedBridge::MD5Unmanaged::ComputeMD5(unsigned __int32 * input)
{ {
#if AVX2 #if AVX2
md5(input + 0 * 8 * 8, output + 0 * 8); md5(input + 0 * 8 * 8);
md5(input + 1 * 8 * 8, output + 1 * 8); md5(input + 1 * 8 * 8);
#elif SIMD #elif SIMD
md5(input + 0 * 8 * 4, output + 0 * 4); md5(input + 0 * 8 * 4);
md5(input + 1 * 8 * 4, output + 1 * 4); md5(input + 1 * 8 * 4);
if (input[2 * 8 * 4] != 0) if (input[2 * 8 * 4] != 0)
{ {
md5(input + 2 * 8 * 4, output + 0 * 4); md5(input + 2 * 8 * 4);
md5(input + 3 * 8 * 4, output + 1 * 4); md5(input + 3 * 8 * 4);
} }
#else #else
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
md5(input + i * 8, output + i); md5(input + i * 8);
} }
#endif #endif
} }

@ -12,7 +12,7 @@ namespace WhiteRabbitUnmanagedBridge {
{ {
public: public:
literal int PhrasesPerSet = PHRASES_PER_SET; literal int PhrasesPerSet = PHRASES_PER_SET;
static void ComputeMD5(unsigned int* input, unsigned int* output); 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 permutationOffset, int numberOfCharacters, int numberOfWords);
}; };
} }

@ -29,10 +29,14 @@ typedef __m256i MD5Vector;
input[offset + 0 * 8]) input[offset + 0 * 8])
#define WRITE_TO_OUTPUT(a, output) \ #define WRITE_TO_OUTPUT(a, output) \
((unsigned __int64*)output)[0] = a.m256i_u64[0]; \ output[7 + 0 * 8] = a.m256i_u32[0]; \
((unsigned __int64*)output)[1] = a.m256i_u64[1]; \ output[7 + 1 * 8] = a.m256i_u32[1]; \
((unsigned __int64*)output)[2] = a.m256i_u64[2]; \ output[7 + 2 * 8] = a.m256i_u32[2]; \
((unsigned __int64*)output)[3] = a.m256i_u64[3]; output[7 + 3 * 8] = a.m256i_u32[3]; \
output[7 + 4 * 8] = a.m256i_u32[4]; \
output[7 + 5 * 8] = a.m256i_u32[5]; \
output[7 + 6 * 8] = a.m256i_u32[6]; \
output[7 + 7 * 8] = a.m256i_u32[7];
#elif SIMD #elif SIMD
@ -55,8 +59,10 @@ typedef __m128i MD5Vector;
input[offset + 0 * 8]) input[offset + 0 * 8])
#define WRITE_TO_OUTPUT(a, output) \ #define WRITE_TO_OUTPUT(a, output) \
((unsigned __int64*)output)[0] = a.m128i_u64[0]; \ output[7 + 0 * 8] = a.m128i_u32[0]; \
((unsigned __int64*)output)[1] = a.m128i_u64[1]; output[7 + 1 * 8] = a.m128i_u32[1]; \
output[7 + 2 * 8] = a.m128i_u32[2]; \
output[7 + 3 * 8] = a.m128i_u32[3];
#else #else
@ -74,7 +80,7 @@ typedef unsigned int MD5Vector;
#define CREATE_VECTOR_FROM_INPUT(input, offset) (input[offset]) #define CREATE_VECTOR_FROM_INPUT(input, offset) (input[offset])
#define WRITE_TO_OUTPUT(a, output) \ #define WRITE_TO_OUTPUT(a, output) \
output[0] = a; output[7] = a;
#endif #endif
#define OP_NEG(a) OP_ANDNOT(a, CREATE_VECTOR(0xffffffff)) #define OP_NEG(a) OP_ANDNOT(a, CREATE_VECTOR(0xffffffff))
@ -179,7 +185,7 @@ static const MD5Parameters Parameters = {
#define Step4(r, a, b, c, d, k, w) StepOuter(r, a, b, OP_ADD(I(c, b, d), OP_ADD(CREATE_VECTOR(k), OP_ADD(a, w)))) #define Step4(r, a, b, c, d, k, w) StepOuter(r, a, b, OP_ADD(I(c, b, d), OP_ADD(CREATE_VECTOR(k), OP_ADD(a, w))))
#define Step4E(r, a, b, c, d, k) StepOuter(r, a, b, OP_ADD(I(c, b, d), OP_ADD(CREATE_VECTOR(k), a))) #define Step4E(r, a, b, c, d, k) StepOuter(r, a, b, OP_ADD(I(c, b, d), OP_ADD(CREATE_VECTOR(k), a)))
void md5(unsigned __int32 * input, unsigned __int32 * output) void md5(unsigned __int32 * input)
{ {
MD5Vector a = CREATE_VECTOR(Parameters.Init[0]); MD5Vector a = CREATE_VECTOR(Parameters.Init[0]);
MD5Vector b = CREATE_VECTOR(Parameters.Init[1]); MD5Vector b = CREATE_VECTOR(Parameters.Init[1]);
@ -262,6 +268,6 @@ void md5(unsigned __int32 * input, unsigned __int32 * output)
a = OP_ADD(CREATE_VECTOR(Parameters.Init[0]), a); a = OP_ADD(CREATE_VECTOR(Parameters.Init[0]), a);
WRITE_TO_OUTPUT(a, output); WRITE_TO_OUTPUT(a, input);
} }
#pragma managed #pragma managed

@ -1,3 +1,3 @@
#pragma once #pragma once
void md5(unsigned int* input, unsigned int* output); void md5(unsigned int* input);

@ -7,18 +7,12 @@
{ {
// It only returns first component of MD5 hash // It only returns first component of MD5 hash
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe uint[] Compute(PhraseSet input) public static unsafe void Compute(PhraseSet input)
{ {
var result = new uint[Constants.PhrasesPerSet]; fixed (uint* inputBuffer = input.Buffer)
fixed (uint* resultPointer = result)
{ {
fixed (long* inputBuffer = input.Buffer) MD5Unmanaged.ComputeMD5(inputBuffer);
{
MD5Unmanaged.ComputeMD5((uint*)inputBuffer, resultPointer);
}
} }
return result;
} }
} }
} }

@ -1,19 +1,20 @@
namespace WhiteRabbit namespace WhiteRabbit
{ {
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices;
// Anagram representation optimized for MD5 // Anagram representation optimized for MD5
internal struct PhraseSet internal struct PhraseSet
{ {
public long[] Buffer; public uint[] Buffer;
public unsafe PhraseSet(Word[] allWords, int[] wordIndexes, ulong[] permutations, int permutationOffset, int numberOfCharacters) public unsafe PhraseSet(Word[] allWords, int[] wordIndexes, ulong[] permutations, int permutationOffset, int numberOfCharacters)
{ {
Debug.Assert(numberOfCharacters + wordIndexes.Length - 1 < 27); Debug.Assert(numberOfCharacters + wordIndexes.Length - 1 < 27);
this.Buffer = new long[4 * Constants.PhrasesPerSet]; this.Buffer = new uint[8 * Constants.PhrasesPerSet];
fixed (long* bufferPointer = this.Buffer) fixed (uint* bufferPointer = this.Buffer)
{ {
fixed (ulong* permutationsPointer = permutations) fixed (ulong* permutationsPointer = permutations)
{ {
@ -21,25 +22,36 @@
{ {
fixed (Word* allWordsPointer = allWords) fixed (Word* allWordsPointer = allWords)
{ {
WhiteRabbitUnmanagedBridge.MD5Unmanaged.FillPhraseSet(bufferPointer, (long*)allWordsPointer, wordIndexesPointer, permutationsPointer, permutationOffset, numberOfCharacters, wordIndexes.Length); WhiteRabbitUnmanagedBridge.MD5Unmanaged.FillPhraseSet((long*)bufferPointer, (long*)allWordsPointer, wordIndexesPointer, permutationsPointer, permutationOffset, numberOfCharacters, wordIndexes.Length);
} }
} }
} }
} }
} }
private static unsafe void FillPhraseSet(long* bufferPointer, long* allWordsPointer, int* wordIndexes, ulong* permutationsPointer, int permutationOffset, int numberOfCharacters, int numberOfWords) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetMD5(int number)
{ {
return this.Buffer[number * 8 + 7];
} }
public unsafe byte[] GetBytes(int number) public unsafe byte[] GetBytes(int number)
{ {
Debug.Assert(number < Constants.PhrasesPerSet); Debug.Assert(number < Constants.PhrasesPerSet);
fixed(long* bufferPointer = this.Buffer) fixed(uint* bufferPointer = this.Buffer)
{ {
var phrasePointer = bufferPointer + 4 * number; var phrasePointer = bufferPointer + 8 * number;
var length = ((uint*)phrasePointer)[7] >> 3; var length = 0;
for (var i = 27; i >= 0; i--)
{
if (((byte*)phrasePointer)[i] == 128)
{
length = i;
break;
}
}
var result = new byte[length]; var result = new byte[length];
for (var i = 0; i < length; i++) for (var i = 0; i < length; i++)
{ {

@ -80,7 +80,7 @@
processor.CheckPhrases(phraseSet => processor.CheckPhrases(phraseSet =>
{ {
var hashesFirstComponents = MD5Digest.Compute(phraseSet); MD5Digest.Compute(phraseSet);
for (var i = 0; i < Constants.PhrasesPerSet; i++) for (var i = 0; i < Constants.PhrasesPerSet; i++)
{ {
Debug.Assert( Debug.Assert(
@ -88,10 +88,11 @@
$"StringsProcessor produced incorrect anagram: {ToString(phraseSet, i)}"); $"StringsProcessor produced incorrect anagram: {ToString(phraseSet, i)}");
if (Vector.EqualsAny(expectedHashesFirstComponents, new Vector<uint>(hashesFirstComponents[i]))) if (Vector.EqualsAny(expectedHashesFirstComponents, new Vector<uint>(hashesFirstComponents[i])))
if (Vector.EqualsAny(expectedHashesFirstComponents, new Vector<uint>(phraseSet.GetMD5(i))))
{ {
var phrase = ToString(phraseSet, i); var phrase = ToString(phraseSet, i);
var hash = ComputeFullMD5(phrase); var hash = ComputeFullMD5(phrase);
Console.WriteLine($"Found phrase for {hash} ({hashesFirstComponents[i]:x8}): {phrase}; time from start is {stopwatch.Elapsed}"); Console.WriteLine($"Found phrase for {hash} ({phraseSet.GetMD5(i):x8}): {phrase}; time from start is {stopwatch.Elapsed}");
} }
} }
}); });

Loading…
Cancel
Save