Асинхронная задача, замедляющая код (хеширование) - PullRequest
0 голосов
/ 28 мая 2018

Я пишу приложение Windows Forms, которое выполняет хэширование списка слов.Чтобы приложение не зависало во время хэширования, я использую асинхронную задачу для хеширования.Однако в результате этого скорость обработки хэшей падает с нескольких тысяч в секунду до примерно 60 в секунду.

Моя функция хеширования выглядит следующим образом

private static string MD5Hash(string word)
{
    var stringBuilder = new StringBuilder();
    var MD5 = new MD5CryptoServiceProvider();
    var bytes = MD5.ComputeHash(new UTF8Encoding().GetBytes(word));

    foreach (var value in bytes)
    {
        stringBuilder.Append(value.ToString("X2"));
    }
    return stringBuilder.ToString();
}

Я реализуюфункция хеширования для хеширования слов из файла, подобного этому (это без использования асинхронной передачи, которая достигает нескольких тысяч хешей в секунду)

private static void DoHashes()
{
    foreach (var word in File.ReadLines("the file path"))
    { 
        File.AppendAllText("the output path",  MD5Hash(word) + Environment.NewLine);         
    }   
}

Затем я использовал асинхронную задачу, чтобы избежать зависания моего приложения для форм Windows, подобного этому(это приводит к падению скорости хэшей примерно до 60 в секунду)

private static async void DoHashes()
{
    await Task.Run(() =>
    {       
        foreach (var word in File.ReadLines("the file path"))
        { 
            File.AppendAllText("the output path",  MD5Hash(word) + Environment.NewLine);      
        }  
    });
}

Как можно избежать этого снижения скорости, не вызывая зависания формы моего окна во время выполнения хэшей?

Ответы [ 3 ]

0 голосов
/ 28 мая 2018

Исходя из того, что вы сказали, что сделали, очень мало замедления.

Возьмите этот базовый случай:

var source = Enumerable.Range(0, 1000000).Select(x => x.ToString()).ToArray();

var sw = Stopwatch.StartNew();
var results = source.Select(x => MD5Hash(x)).ToArray();
sw.Stop();

Console.WriteLine(sw.Elapsed.TotalMilliseconds);

Этот код завершается за 5316.8091 миллисекунд.Это 188 хешей в миллисекунду.

Если я поместил свой тест в метод async и запустил это:

var results = await Task.Run(() => source.Select(x => MD5Hash(x)).ToArray());

... тогда это займет 5531.4172 миллисекунд или около 181 хешаза миллисекунду.

И без async я запускаю это:

var results = Task.Run(() => source.Select(x => MD5Hash(x)).ToArray()).Result;

И оно завершается за 5441.0798 миллисекунд или около 184 хешей за миллисекунду.

Таким образом, я могу только заключить, что код, который вы представили, не вызывает замедления.


Теперь, если вы хотите попробовать и заставить это работать быстрее, попробуйте использоватьРеактивная платформа Microsoft.Затем вы можете написать это:

var sw = Stopwatch.StartNew();

var query =
    from n in Observable.Range(0, 1000000)
    from h in Observable.Start(() => MD5Hash(n.ToString()))
    select new { n, h };

query
    .ToArray()
    .Subscribe(xs =>
    {
        var results = xs.OrderBy(x => x.n).Select(x => x.h).ToArray();

        sw.Stop();

        Console.WriteLine(sw.Elapsed.TotalMilliseconds);
    });

, который работает в нескольких фоновых потоках, но может вернуться обратно в пользовательский интерфейс и выполняется менее чем за 5 секунд.

Просто NuGet "System.Reactive"и добавьте using System.Reactive.Linq к вашему коду.

0 голосов
/ 28 мая 2018

Я провел этот тест с использованием приложения WPF.В моей тестовой среде я использовал файл с 5000 строками.Ниже приведены ответы

|-------------------------------------------------------------------------------------
|#        Description                                            Time Taken (in secs)
|-------------------------------------------------------------------------------------
|1    Without Async/Await (As mentioned in the question)            144.933
|2    With Async/Await (As mentioned in the question)               145.563
|3    Using StringBuilder and writing to file only once             0.143
|4    With Async/Await and set ConfigureAwait to false              90.657
|-------------------------------------------------------------------------------------

Если вы видите результат, между Тестом № 1 и Тестом № 2 нет существенных различий, поэтому перенос в async-await не должен иметь значения в вашем сценарии.

Ниже приведен код для теста № 3 и № 4

теста 3 (использование StringBuilder и запись в файл только один раз)

private static async void DoHashes()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    await Task.Run(() =>
    {
        StringBuilder sb = new StringBuilder();
        foreach (var word in File.ReadLines(Input file path))
        {
           sb.AppendLine(MD5Hash(word));
        }

       File.AppendAllText(Output file path, sb.ToString());
    });

    sw.Stop();

    MessageBox.Show("Time Taken by Do Hashes : " + (sw.ElapsedMilliseconds / 1000.0) + " secs");
}

Результатэтот тест 0,143 (в 1000 раз лучше, чем тест № 1 и № 2), поскольку дескриптор файла не получен процессом несколько раз.

тест 4 (с асинхронным ожиданием / ожиданием)и установите для ConfigureAwait значение false)

private static async void DoHashes()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    await Task.Run(() =>
    {
        foreach (var word in File.ReadLines(Input file path)
        {
           File.AppendAllText(Output file path, MD5Hash(word) + Environment.NewLine);
        }
    }).ConfigureAwait(false);

    sw.Stop();

    MessageBox.Show("Time Taken by Do Hashes : " + (sw.ElapsedMilliseconds / 1000.0) + " secs");
}

Установка ConfigureAwait попыток на NOT для перенаправления продолжения обратно в исходный захваченный контекст, чтобы вы могли видеть, что производительность увеличилась--- Сравните с тестом № 1 и тестом № 2, это займет 40% меньше времени (занимает всего 90,657 секунд).

0 голосов
/ 28 мая 2018

Если вам не нужно взаимодействие с Form во время / после хеширования, не используйте асинхронное.Задача достаточно.

private static void DoHashes()
{
    Task.Run(() =>
    {       
        foreach (var word in File.ReadLines("the file path"))
        { 
            File.AppendAllText("the output path",  MD5Hash(word) + Environment.NewLine);      
        }  
    });
}

Также выложу static s

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...