Многопоточная десериализация с использованием BinaryFormatter или MessagePack не дает ожидаемого прироста производительности, и я пытаюсь понять, почему. Десериализация всех фрагментов одновременно происходит примерно в 5 раз медленнее, чем десериализация одного фрагмента для этой настройки.
Вот мой тестовый код с различными типами многопоточности. Используются Tasks, Threads и ParallelForEach, и самым быстрым на сегодняшний день является Threads.
Часки считываются перед выполнением десериализации, что означает, что никакая работа, связанная с вводом-выводом, не выполняется, и куски делятся на количество ядра у меня есть.
Код: Выделить всё
using System.Collections;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializerDeserializeTests
{
public class Program
{
static void Main(string[] args)
{
int threads = 8;
int entries = 500000;
string directory = "data";
if(!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
Hashtable hashtable = GenerateHashtable(entries);
List chunks = ChunkHashtable(hashtable, threads);
Export(chunks, directory);
}
else
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List chunks = Directory.GetFiles(directory).Select(File.ReadAllBytes).ToList();
Console.WriteLine("Read: " + stopwatch.Elapsed.TotalSeconds);
stopwatch.Stop();
DeserializeParallelForEach(chunks).Clear();
DeserializeParallelForEachOneChunk(chunks).Clear();
DeserializeTasks(chunks).Clear();
DeserializeTasksOneChunk(chunks).Clear();
DeserializeThreads(chunks).Clear();
DeserializeThreadsOneChunk(chunks).Clear();
}
}
public static List DeserializeThreadsOneChunk(List chunks)
{
chunks = new List() { chunks[0] };
Console.WriteLine("----- DeserializeThreadsOneChunk -----");
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
ConcurrentBag hashtables = new ConcurrentBag();
List threads = new List();
foreach (byte[] chunk in chunks)
{
Thread thread = new Thread(() =>
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
using (MemoryStream memoryStream = new MemoryStream(chunk))
{
#pragma warning disable SYSLIB0011 // Type or member is obsolete
hashtables.Add((Hashtable)binaryFormatter.Deserialize(memoryStream));
#pragma warning restore SYSLIB0011 // Type or member is obsolete
}
});
threads.Add(thread);
thread.Start();
}
foreach (var thread in threads)
thread.Join();
Console.WriteLine("Deserialize: " + stopwatch.Elapsed.TotalSeconds);
stopwatch.Stop();
return hashtables.ToList();
}
public static List DeserializeThreads(List chunks)
{
Console.WriteLine("----- DeserializeThreads -----");
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
ConcurrentBag hashtables = new ConcurrentBag();
List threads = new List();
foreach (byte[] chunk in chunks)
{
Thread thread = new Thread(() =>
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
using (MemoryStream memoryStream = new MemoryStream(chunk))
{
#pragma warning disable SYSLIB0011 // Type or member is obsolete
hashtables.Add((Hashtable)binaryFormatter.Deserialize(memoryStream));
#pragma warning restore SYSLIB0011 // Type or member is obsolete
}
});
threads.Add(thread);
thread.Start();
}
foreach (var thread in threads)
thread.Join();
Console.WriteLine("Deserialize: " + stopwatch.Elapsed.TotalSeconds);
stopwatch.Stop();
return hashtables.ToList();
общедоступный статический список< Hashtable> DeserializeTasksOneChunk(List chunks)
{
chunks = new List() { chunks[0] };
Console.WriteLine("-- --- DeserializeTasksOneChunk -----");
Секундомер stopwatch = new Stopwatch();
stopwatch.Start();
List задач = new List();
foreach (фрагмент byte[] в кусках)
{
Task Task = Task.Run(() =>
{
BinaryFormatterbinaryFormatter = new BinaryFormatter();
using (MemoryStream MemoryStream = new MemoryStream(chunk))
{
#pragma предупреждение отключить SYSLIB0011 // Тип или член устаревший
return (Hashtable)binaryFormatter.Deserialize(memoryStream);
#pragma alertrestore SYSLIB0011 // Тип или член устарел
});
Tasks.Add(task);
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Десериализовать: " + stopwatch.Elapsed.TotalSeconds);
stopwatch.Stop();
return Tasks.Select(x => x.Result).ToList();
public static List DeserializeTasks(List chunks)
{
Console.WriteLine("----- DeserializeTasks -----");
Секундомер секундомер = новый Stopwatch();
stopwatch.Start();
List задачи = новый List();
foreach (кусок byte[] в кусках)
{
Task Task = Task.Run(() =>
{
BinaryFormatterbinaryFormatter = new BinaryFormatter();
using (MemoryStream MemoryStream = new MemoryStream(chunk))
{
#pragma предупреждение отключить SYSLIB0011 // Тип или элемент устарел
return (Hashtable)binaryFormatter.Deserialize(memoryStream);< br />#pragma alertrestore SYSLIB0011 // Тип или элемент устарел
});
Tasks.Add(task);
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Десериализовать: " + stopwatch.Elapsed.TotalSeconds);
stopwatch.Stop();
return Tasks.Select(x => x.Result).ToList();
public static List DeserializeParallelForEachOneChunk(List chunks)
{
chunks = new List() { chunks[0] };
Console.WriteLine("----- DeserializeParallelForEachOneChunk ---- -");
Секундомер stopwatch = new Stopwatch();
stopwatch.Start();
ConcurrentBag hashtables = new ConcurrentBag();
Parallel.ForEach(chunks, (chunk) =>
{
BinaryFormatterbinaryFormatter = new BinaryFormatter();
using (MemoryStream MemoryStream = new MemoryStream(chunk))
{
#pragma предупреждение отключить SYSLIB0011 // Тип или элемент устарел
hashtables.Add((Hashtable)binaryFormatter.Deserialize(memoryStream));
#pragma предупреждение восстановить SYSLIB0011 // Тип или элемент является устаревший
});
Console.WriteLine("Десериализовать: " + stopwatch.Elapsed.TotalSeconds);
stopwatch.Stop();
return hashtables.ToList();
public static List DeserializeParallelForEach(List chunks)
{
Console.WriteLine( "----- DeserializeParallelForEach -----");
Секундомер stopwatch = new Stopwatch();
stopwatch.Start();
ConcurrentBag hashtables = новый ConcurrentBag();
Parallel.ForEach(chunks, (chunk) =>
{
BinaryFormatterbinaryFormatter = new BinaryFormatter();
using (MemoryStream MemoryStream = new MemoryStream(chunk))
{
#pragma предупреждение отключить SYSLIB0011 // Введите или элемент устарел
hashtables.Add((Hashtable)binaryFormatter.Deserialize(memoryStream));
#pragma alertrestore SYSLIB0011 // Тип или элемент устарел
});
Console.WriteLine("Десериализовать: " + stopwatch.Elapsed.TotalSeconds);
stopwatch.Stop();
return hashtables.ToList();< br />
public static void Export(List chunks, stringdirectory)
{
Console.WriteLine("Экспорт фрагментов");
foreach (Чанк хеш-таблицы в кусках)
{
BinaryFormatterbinaryFormatter = new BinaryFormatter();
using (FileStream fileStream = new FileStream(directory + "\\chunk-" + Guid.NewGuid() + ".dat", FileMode.Create))
{
using(BinaryWriterbinaryWriter = new BinaryWriter(fileStream))
{
#pragma предупреждение отключить SYSLIB0011 // Тип или член устаревший
binaryFormatter.Serialize(binaryWriter.BaseStream, chunk);
#pragma alertrestore SYSLIB0011 // Тип или член устарел
public static List ChunkHashtable(Hashtable Hashtable, int NumberOfChunks)
{
Console.WriteLine("Разбивка хэш-таблицы");
int chunkSize = hashtable.Count / NumberOfChunks;
int rester = hashtable.Count % NumberOfChunks;
var chunks = new List(numberOfChunks);
IDictionaryEnumerator enumerator = hashtable.GetEnumerator();
int elementsAdded = 0;
for (int i = 0; я Результат:
[code]Read: 0,0298205
----- DeserializeParallelForEach -----
Deserialize: 0,5175991
----- DeserializeParallelForEachOneChunk -----
Deserialize: 0,0809796
----- DeserializeTasks -----
Deserialize: 0,4418913
----- DeserializeTasksOneChunk -----
Deserialize: 0,193335
----- DeserializeThreads -----
Deserialize: 0,3709102
----- DeserializeThreadsOneChunk -----
Deserialize: 0,0767091
Note: I know that BinaryFormatter shouldn't be used anymore but for my case it's fine.
Note2: MessagePack is even slower than BinaryFormatter and thus not included in my tests.
Источник: https://stackoverflow.com/questions/781 ... yformatter