Моим следующим предположением о том, как сделать это еще быстрее, было, естественно, распараллелить код, анализируя несколько его частей одновременно. Поскольку ни один XML-элемент не опирался на предыдущий, я мог безопасно анализировать равное (или почти равное) количество элементов в каждом потоке, а затем объединять коллекции.
И хотя я знаю, что существуют определенные накладные расходы на запуск потоков (а в моем случае также на разделение работы на потоки), я все же ожидал, что параллельная версия превзойдет по производительности последовательную версию на моем ПК (32 логических ядра, 24 физических ядра). Но какое бы количество потоков я ни пробовал для тестирования, оно всегда было медленнее.
Код: Выделить всё
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
// Nullable = tag is not guaranteed to appear in an XML element.
// Empty tags () would instead produce string.Empty.
public struct RawParameterData {
public ulong Id;
public string Name;
public ulong? OwnerComponentId;
public string Type;
public string Value;
public bool? Implicit;
public ulong ParentId;
public bool? Custom;
public override string ToString() => $"{Id} {OwnerComponentId} {Name} {Type} {Value} {Implicit} {ParentId} {Custom}";
}
// Just a set of methods to parse different data types from span of raw bytes
public static unsafe class RawPointerExtensions {
private static readonly Vector128 V16Lt = Vector128.Create((byte)' {
var p = (byte*)basePtr;
var a = borders[i];
var b = borders[i + 1];
threadLists[i] = RawPointerParser.ParseRange(p, a, b);
});
foreach (var t in threadLists) results.AddRange(t);
return results;
}
}
private static unsafe List FindChunkStarts(byte* ptr, long n, int threadCount, long start) {
var end = n - 10;
var work = end - start;
var slice = work / threadCount;
var starts = new List(threadCount + 1) { start };
for (var i = 1; i < threadCount; i++)
starts.Add(AlignToItem(ptr, start + slice * i, end));
starts.Add(end);
return starts;
}
private static unsafe long AlignToItem(byte* basePtr, long pos, long max) {
var p = basePtr + pos;
var end = basePtr + max - 5;
var m0 = Vector256.Create(Pattern[0]);
while (p < end) {
var v = Avx.LoadVector256(p);
var eq = Avx2.CompareEqual(v, m0);
var mask = Avx2.MoveMask(eq);
if (mask != 0) {
while (mask != 0) {
var idx = BitOperations.TrailingZeroCount(mask);
var q = p + idx;
if (q[4] == Pattern[4])
return q - basePtr + 1;
mask &= mask - 1;
}
}
p += 32;
}
return max;
}
}
[MemoryDiagnoser]
public class SampleParserBenchmark {
public string FilePath = @"C:\Parameter.xml";
[Benchmark] public List RawPointer() => RawPointerParser.ProcessFile(FilePath);
[Benchmark] public List RawPointerParallel() => RawPointerParserParallel.ProcessFile(FilePath);
[Benchmark] public List RawPointerParallel8() => RawPointerParserParallel.ProcessFile(FilePath, 8);
[Benchmark] public List RawPointerParallel16() => RawPointerParserParallel.ProcessFile(FilePath, 16);
}
public class Program {
public static void Main() => BenchmarkRunner.Run();
}
Код: Выделить всё
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------------------- |---------:|---------:|---------:|----------:|----------:|---------:|----------:|
| RawPointer | 21.28 ms | 0.413 ms | 0.701 ms | 1812.5000 | 1781.2500 | 937.5000 | 40.17 MB |
| RawPointerParallel | 26.65 ms | 0.508 ms | 0.522 ms | 1593.7500 | 1562.5000 | 687.5000 | 53.86 MB |
| RawPointerParallel8 | 23.29 ms | 0.550 ms | 1.623 ms | 1750.0000 | 1718.7500 | 812.5000 | 47.93 MB |
| RawPointerParallel16 | 23.03 ms | 0.556 ms | 1.640 ms | 1750.0000 | 1718.7500 | 750.0000 | 48.44 MB |
Файл, о котором идет речь, если кто-то хочет повторить мой тест (16 МБ): https://drive.google.com/file/d/1vI6g9O ... sp=sharing
Вопросы: что приводит к замедлению работы моего параллельного подхода и что я могу сделать, чтобы устранить эти узкие места? Неужели самое эффективное решение — это просто потратить 20 мс на один поток?
Подробнее здесь: https://stackoverflow.com/questions/798 ... ork-slower
Мобильная версия