C# - 程序正在快速减速,为什么?

时间:2016-11-14 20:53:20

标签: c# performance console

我做了一个非常简单的暴力密码破解程序(我知道这很糟糕),我有一个运行的线程,它输出每秒有多少组合,以及它尝试了多少组合。

它开始瞬间开始每秒2000组合,然后迅速下降到150左右,然后在一小时左右后慢慢减少到1。

这是我运行的主题:

Thread thread = new Thread(new ThreadStart(UpdateGuessCount));
thread.Start();
tring currentGuess = "a";
while (currentGuess != password)
{
    currentGuess = Shift(currentGuess);
    guesses++;
    guessesPerSecond++;
}

static void UpdateGuessCount()
{
    while (true)
    {
        mre.WaitOne();
        Console.Clear();
        Console.WriteLine("Current guesses: " + guesses);
        Console.WriteLine("Current guesses per second: " + guessesPerSecond);
        guessesPerSecond = 0;
        Thread.Sleep(1000);
    }
}

“Shift”方法更改组合,向上移动。例如,Shift(“a”)将变为“b”,依此类推。

为什么会发生这种情况,有什么办法可以阻止它发生/加快速度?

提前致谢!

编辑:

转换源代码:

static string Shift(string toShift)
{
    char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'w', 'x', 'y', 'z',
                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z',
                '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
    char[] shiftChars = new char[toShift.Length + 1];
    Array.Copy(toShift.ToCharArray(), shiftChars, toShift.Length);
    shiftChars[shiftChars.Length - 1] = ' ';
    int index = 0;
    char chr = shiftChars[index];

    if (chr == '0')
    {
        while (chr == '0')
        {
            shiftChars[index] = 'a';
            index++;
            chr = shiftChars[index];
        }
        if (shiftChars[index] != ' ')
        {
            int charIndex = 0;
            foreach (char c in chars)
            {
                if (c == chr)
                {
                    shiftChars[index] = chars[charIndex + 1];
                }
                charIndex++;
            }
        }
        else
        {
            shiftChars[index] = 'a';
        }
    }

    else
    {
        int charIndex = 0;
        foreach (char c in chars)
        {
            if (c == chr)
            {
                shiftChars[index] = chars[charIndex + 1];
            }
            charIndex++;
        }
    }

    string returnString = "";
    foreach (char c in shiftChars)
    {
        returnString = returnString + c;
    }

    return returnString;
 }

1 个答案:

答案 0 :(得分:1)

以下程序维护您的原始算法,并在我的笔记本电脑上以~20M比较/秒运行,并且不会随着时间的推移而降级。我和原版的速度相同~2k / sec,并且快速降级。

您的实施的核心问题是过多的字符串分配导致了大量的GC活动。罪魁祸首是Shift()方法中的以下行:

char[] shiftChars = new char[toShift.Length + 1];

在每次调用Shift()时,它会增加猜测字符串的长度。在20k比较之后,你有一个20k长的猜测字符串,只使用了前几个字符。

Shift()也将字符串作为参数并返回字符串。这意味着每次转移呼叫的分配。您已经使用char []来完成工作,所以只需接受并返回它们即可。这样我们就可以预先分配猜测缓冲区,只需移动里面的字符。

static string Shift(string toShift);

替换为:

static void Shift(ref char[] shiftChars, ref int tailIndex)

根据您想要转移的值,您可以循环查找当前索引。我创建了一个以“'值”为基础的地图。这会将下一个索引返回到chars []数组中。所以给出了' D'地图返回' E'的索引。可能有更好的方法,但这节省了大量的处理时间。

...
int charIndex = 0;
foreach (char c in chars)
{
   if (c == chr)
   {
      shiftChars[index] = chars[charIndex + 1];
   }
   charIndex++;
}
...

替换为:

shiftChars[index] = chars[charsLookupNext[chr]]; 

VC中的CPU诊断在Shift()中显示65%的时间,在checkBuffers()中显示30%。我知道checkBuffers()很快,因此很适合Shift()。

using System;
using System.Collections.Generic;
using System.Threading;

namespace PwdCrackPerf
{
    class Program
    {
        private static UInt64 guesses = 0;       
        private static char[] currentGuessBuffer;
        private static readonly char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z',
            '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};

        private static readonly Dictionary<char,int> charsLookupNext = new Dictionary<char, int>() {{'a',1}, {'b',2}, {'c',3}, {'d',4}, {'e',5}, {'f',6}, {'g',7},
        { 'h',8}, {'i',9}, {'j',10}, {'k',11}, {'l',12}, {'m',13}, {'n',14}, {'o',15}, {'p',16}, {'q',17}, {'r',18}, {'s',19}, {'t',20}, {'w',21}, {'x',22}, {'y',23},
        { 'z',24}, {'A',25}, {'B',26}, {'C',27}, {'D',28}, {'E',29}, {'F',30}, {'G',31}, {'H',32}, {'I',33}, {'J',34}, {'K',35}, {'L',36}, {'M',37}, {'N',38}, {'O',39},
        { 'P',40}, {'Q',41}, {'R',42}, {'S',43}, {'T',44}, {'W',45}, {'X',46}, {'Y',47}, {'Z',48}, {'1',49}, {'2',50}, {'3',51}, {'4',52}, {'5',53}, {'6',54}, {'7',55},
        { '8',56}, {'9',57}, {'0',58}};

        private static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        static void Main(string[] args)
        {
           var thread = new Thread(new ThreadStart(UpdateGuessCount));
           thread.Start();

           var passwordBuffer = args[0].ToCharArray();
           currentGuessBuffer = new char[passwordBuffer.Length];
           currentGuessBuffer[0] = chars[0];

           var tailIndex = 0;
           while ( !checkBuffers(passwordBuffer, currentGuessBuffer))
           {
               Shift(ref currentGuessBuffer, ref tailIndex);
               guesses++;
           }
           cancellationTokenSource.Cancel();
           Console.WriteLine($"Password: {new string(currentGuessBuffer)}");
           Console.ReadKey();      
          return;
       }

       static bool checkBuffers(char[] charBuffer1, char[] charBuffer2)
       {
           var index = 0;
           while ((charBuffer1[index] == charBuffer2[index]) && ++index < charBuffer1.Length) ;
           return index == charBuffer1.Length;
       }       

       static void Shift(ref char[] shiftChars,ref int tailIndex)
       {            
           int index = 0;
           char chr = shiftChars[index];

           if (chr == chars[chars.Length-1])
           {
               while (chr == chars[chars.Length-1])
               {
                  shiftChars[index++] = chars[0];
                  if (index < shiftChars.Length)
                      chr = shiftChars[index];
                  else break;
               }
               if (index <= tailIndex)
                   shiftChars[index] = chars[charsLookupNext[chr]];                   
               else if(tailIndex+1<shiftChars.Length)
                   shiftChars[++tailIndex] = chars[0];                
            }
            else
                shiftChars[index] = chars[charsLookupNext[chr]];                                
        }

        static void UpdateGuessCount()
        {
            UInt64 lastGuessCount=0;
            while (!cancellationTokenSource.Token.IsCancellationRequested)
            {
               //Just for ballpark
               Console.Clear();
               Console.WriteLine($"Current guesses: {guesses}");
               Console.WriteLine($"Current guesses per second: {guesses-lastGuessCount}");
               Console.WriteLine($"Current guess:{new string(currentGuessBuffer)}");
               lastGuessCount = guesses;
               Thread.Sleep(1000);
           }
       }
   }
}