Как можно ещё оптимизировать мой код?
Код, вычисляющий количество бедрока. Как возможно максимально быстро вычислять? Как сделать MultiThread ещё не придумал.
В мэйне идёт загрузка из файла аргументов, указывающих на: Прогресс, способ вычислений.
public struct Program
{
public static void Main()
{
List<long> args = File.ReadAllText("C:\\p.txt").Split(',').Select(long.Parse).ToList();
Console.WriteLine("Stats was successful. x - " + args[0] + ", State - " + args[4] + "/" + args[7] + ", Count - " + args[1]);
switch (args[8])
{
case 0:
new OneThreadCPU().Start(args);
break;
case 1:
new MoreThreadCPU().Start();
break;
}
}
}
public struct OneThreadCPU
{
private const int MaxBlock = 0x1C9C38;
public void Start(List<long> args)
{
SetThreadAffinityMask(GetCurrentThread(), (UIntPtr)1);
Help h = new Help();
h.Load(new Info((int)args[0], args[1], new Point((int)args[2], (int)args[3], (int)args[4]), new Point((int)args[5], (int)args[6], (int)args[7])));
for (; h.i.x < MaxBlock; h.i.x++)
{
var z = -1875000;
var startTime = DateTime.Now;
for (; z < MaxBlock; z++) h.i.count += h.GetBedrockChunk(h.i.x, z);
h.Save(@"C:\\p.txt", h.i.x + 1, h.i.count, h.i.min, h.i.max, startTime, 0, 0);
}
}
[DllImport("kernel32.dll")]
static extern UIntPtr SetThreadAffinityMask(IntPtr hThread, UIntPtr dwThreadAffinityMask);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThread();
}
public struct Help
{
private const int MaxBlock = 0x1C9C38;
public Info i;
private readonly BedrockGen Bd = new();
private string DateTimes() => "[" + DateTime.Now.ToString("yy.MM.dd.HH:mm:ss") + "] ";
public long GetBedrockChunk(int x, int z) //-1875000,0,0,0,1000,0,0,0,0,0
{
var countBedrockInChunk = 0;
var chunkSeed = Bd.rSFC(x, z);
var vx = x >> 4;
var vz = z >> 4;
for (var y = (byte)1; y < 5; y++)
for (var bx = vx; bx < vx + 16; bx++)
for (var bz = vz; bz < vz + 16; bz++)
if (Bd.bO112(bx, y, bz, chunkSeed))
countBedrockInChunk++;
if (countBedrockInChunk > i.max.count)
{
Send(ConsoleColor.Green,
"Max " + "[" + countBedrockInChunk + "/" + i.max.count + "] [" + i.x + "|" + z
+ "] [" + (i.count + countBedrockInChunk).ToString("#,#", new CultureInfo("de-DE")) + "]");
i.max = new Point(x, z, countBedrockInChunk);
}
else if (countBedrockInChunk < i.min.count)
{
Send(ConsoleColor.Red,
"Min " + "[" + countBedrockInChunk + "/" + i.min.count + "] [" + x + "|" + z
+ "] [" + (i.count + countBedrockInChunk).ToString("#,#", new CultureInfo("de-DE")) + "]");
i.min = new Point(x, z, countBedrockInChunk);
}
return countBedrockInChunk;
}
private void Send(ConsoleColor color, string message)
{
Console.ForegroundColor = color;
Console.WriteLine(DateTimes() + message);
Console.ResetColor();
}
public void Load(Info i)
{
this.i = i;
}
public void Save(string path, int x, long countBedrockArg, Point coordinatesMinBedrockCountArg, Point coordinatesMaxBedrockCountArg, DateTime dateStart, int firstArg, int secondArg)
{
File.WriteAllText(path,
string.Join(',',
new List<long>{
x, countBedrockArg, coordinatesMinBedrockCountArg.x, coordinatesMinBedrockCountArg.z,
coordinatesMinBedrockCountArg.count, coordinatesMaxBedrockCountArg.x,
coordinatesMaxBedrockCountArg.z,
coordinatesMaxBedrockCountArg.count, firstArg, secondArg
}.Select(x => x.ToString())));
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"{DateTimes()}Save [{x}({ Math.Round((float)(100 - (float)Math.Abs(x) / MaxBlock * 100 + (x > 0 ? 50 : 0)), 5, MidpointRounding.AwayFromZero)}%)] [{(new TimeSpan((DateTime.Now - dateStart).Ticks).ToString("mm'm 'ss's 'fff'n'").Replace("00s ", "").Replace("00m ", "").Replace("000n", ""))}] [{i.count.ToString("#,#", new CultureInfo("de-DE"))}]");
Console.ResetColor();
}
}
internal struct BedrockGen
{
private const long q = 0x4F9939F508, w = 0x1EF1565BD5, e = 0x5DEECE66D, g = 0xFFFFFFFFFFFF;
private const int t = 0xF, a1 = 0x10, a2 = 0x11;
internal long rSFC(in int x, in int z) => ((x * q + z * w) ^ e) & g;
private bool r(in long rs, in long a, in long b, in int y) => ((((rs * a + b) & g) >> a2) % 5) >= y;
private int pCICN(in int x, in int y, in int z) => ((z * a1) + x) * 4 + (3 - y);
internal bool bO112(in int x, in byte y, in int z, in long cs)
{
var pi = pCICN(x & t, y - 1, z & t);
return r(cs, A_OW_112[pi], B_OW_112[pi], y);
}
}
public struct Info
{
public int x;
public long count;
public Point min, max;
public Info(int x, long count, Point min, Point max)
{
this.x = x;
this.count = count;
this.min = min;
this.max = max;
}
}
public struct Point
{
public readonly int count;
public readonly int x;
public readonly int z;
public Point(int x, int z, int count)
{
this.x = x;
this.z = z;
this.count = count;
}
}
Ответы (1 шт):
Я не знаю, насколько получилось правильно или нет, есть ощущение, что оно считает не так, как в однопоточке, но результат в Save улетает правильный. Работает примерно в 10 раз быстрее на моих 8 ядрах, но это будет сильно зависеть от возможностей процессора. Само собой, это не предел, и возможно решение вам не подойдет, но метод решения пригодится.
Как я понял, из-за того что i.max и i.min присваиваются не в том порядке, в котором это происходит при синхронных вычислениях, то происходит ряд лишних вычислений, либо просто происходит лишний вывод min/max в консоль, а с вычислениями все в порядке, но это вам будет виднее при анализе результатов.
Вы переборщили со структурами, используйте статические классы, старайтесь писать аккуратный код там, где не требуется суперпроизводительность.
class Program
{
static void Main()
{
long[] args = "-1875000,0,0,0,1000,0,0,0,0,0".Split(',').Select(long.Parse).ToArray();
Console.WriteLine("Stats was successful. x - " + args[0] + ", State - " + args[4] + "/" + args[7] + ", Count - " + args[1]);
OneThreadCPU.Start(args);
}
}
public static class OneThreadCPU
{
private static readonly ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 };
private const int MaxBlock = 0x1C9C38;
[DllImport("kernel32.dll")]
static extern UIntPtr SetThreadAffinityMask(IntPtr hThread, UIntPtr dwThreadAffinityMask);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThread();
public static void Start(long[] args)
{
//SetThreadAffinityMask(GetCurrentThread(), (UIntPtr)1);
Help h = new Help();
h.Load(new Info((int)args[0], args[1], new Point((int)args[2], (int)args[3], (int)args[4]), new Point((int)args[5], (int)args[6], (int)args[7])));
for (; h.i.x < MaxBlock; h.i.x++)
{
var startTime = DateTime.Now;
//for (int z = -1875000; z < MaxBlock; z++)
// h.i.count += h.GetBedrockChunk(h.i.x, z);
Parallel.For(-1875000, MaxBlock, parallelOptions, z =>
{
Interlocked.Add(ref h.i.count, h.GetBedrockChunk(h.i.x, z));
});
h.Save(@"p.txt", h.i.x + 1, h.i.count, h.i.min, h.i.max, startTime, 0, 0);
}
}
}
public struct Help
{
private static readonly CultureInfo deDE = new CultureInfo("de-DE");
private const int MaxBlock = 0x1C9C38;
public Info i;
private string DateTimes() => "[" + DateTime.Now.ToString("yy.MM.dd.HH:mm:ss") + "] ";
private static readonly object _lock = new object();
public long GetBedrockChunk(int x, int z) //-1875000,0,0,0,1000,0,0,0,0,0
{
int countBedrockInChunk = 0;
long chunkSeed = BedrockGen.rSFC(x, z);
int vx = x >> 4;
int vz = z >> 4;
for (byte y = 1; y < 5; y++)
for (int bx = vx; bx < vx + 16; bx++)
for (int bz = vz; bz < vz + 16; bz++)
if (BedrockGen.bO112(bx, y, bz, chunkSeed))
countBedrockInChunk++;
lock (_lock)
{
if (countBedrockInChunk > i.max.count)
{
Send(ConsoleColor.Green,
"Max " + "[" + countBedrockInChunk + "/" + i.max.count + "] [" + i.x + "|" + z
+ "] [" + (i.count + countBedrockInChunk).ToString("#,#", deDE) + "]");
i.max = new Point(x, z, countBedrockInChunk);
}
else if (countBedrockInChunk < i.min.count)
{
Send(ConsoleColor.Red,
"Min " + "[" + countBedrockInChunk + "/" + i.min.count + "] [" + x + "|" + z
+ "] [" + (i.count + countBedrockInChunk).ToString("#,#", deDE) + "]");
i.min = new Point(x, z, countBedrockInChunk);
}
}
return countBedrockInChunk;
}
private void Send(ConsoleColor color, string message)
{
Console.ForegroundColor = color;
Console.WriteLine(DateTimes() + message);
Console.ResetColor();
}
public void Load(Info i)
{
this.i = i;
}
public void Save(string path, int x, long countBedrockArg, Point coordinatesMinBedrockCountArg, Point coordinatesMaxBedrockCountArg, DateTime dateStart, int firstArg, int secondArg)
{
File.WriteAllText(path,
string.Join(',',
new long[]{
x, countBedrockArg, coordinatesMinBedrockCountArg.x, coordinatesMinBedrockCountArg.z,
coordinatesMinBedrockCountArg.count, coordinatesMaxBedrockCountArg.x,
coordinatesMaxBedrockCountArg.z,
coordinatesMaxBedrockCountArg.count, firstArg, secondArg
}));
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"{DateTimes()}Save [{x}({ Math.Round((float)(100 - (float)Math.Abs(x) / MaxBlock * 100 + (x > 0 ? 50 : 0)), 5, MidpointRounding.AwayFromZero)}%)] [{(new TimeSpan((DateTime.Now - dateStart).Ticks).ToString("mm'm 'ss's 'fff'n'").Replace("00s ", "").Replace("00m ", "").Replace("000n", ""))}] [{i.count.ToString("#,#", new CultureInfo("de-DE"))}]");
Console.ResetColor();
}
}
internal static class BedrockGen
{
#region Vars
private static readonly long[] A_OW_112 =
{
// ...
};
private static readonly long[] B_OW_112 =
{
// ...
};
#endregion
private const long q = 0x4F9939F508, w = 0x1EF1565BD5, e = 0x5DEECE66D, g = 0xFFFFFFFFFFFF;
private const int t = 0xF, a1 = 0x10, a2 = 0x11;
internal static long rSFC(in int x, in int z) => ((x * q + z * w) ^ e) & g;
private static int pCICN(in int x, in int y, in int z) => ((z * a1) + x) * 4 + (3 - y);
private static bool r(in long rs, in long a, in long b, in int y) => ((((rs * a + b) & g) >> a2) % 5) >= y;
internal static bool bO112(in int x, in byte y, in int z, in long cs)
{
var pi = pCICN(x & t, y - 1, z & t);
return r(cs, A_OW_112[pi], B_OW_112[pi], y);
}
}
public struct Info
{
public int x;
public long count;
public Point min, max;
public Info(int x, long count, Point min, Point max)
{
this.x = x;
this.count = count;
this.min = min;
this.max = max;
}
}
public struct Point
{
public readonly int count;
public readonly int x;
public readonly int z;
public Point(int x, int z, int count)
{
this.x = x;
this.z = z;
this.count = count;
}
}
A_OW_112 и B_OW_112 выкинул из кода, не влезали в ответ.
Вывод в консоль без многопоточки был 26-28 сек, с многопоточкой 3 сек, то есть ~x10
Stats was successful. x - -1875000, State - 1000/0, Count - 0
[21.12.20.20:35:12] Max [524/0] [-1875000|0] [524]
[21.12.20.20:35:12] Min [508/1000] [-1875000|1] [1.032]
[21.12.20.20:35:12] Max [538/524] [-1875000|2] [1.570]
[21.12.20.20:35:12] Min [502/508] [-1875000|5] [3.121]
[21.12.20.20:35:12] Min [479/502] [-1875000|6] [3.600]
[21.12.20.20:35:12] Min [473/479] [-1875000|9] [6.654]
[21.12.20.20:35:12] Max [540/538] [-1875000|14] [9.750]
[21.12.20.20:35:12] Max [542/540] [-1875000|-1640619] [19.520]
[21.12.20.20:35:12] Max [545/542] [-1875000|-1406235] [55.907]
[21.12.20.20:35:12] Max [552/545] [-1875000|-1406212] [103.443]
[21.12.20.20:35:12] Min [468/473] [-1875000|-703105] [132.064]
[21.12.20.20:35:12] Max [557/552] [-1875000|-234363] [137.208]
[21.12.20.20:35:12] Max [561/557] [-1875000|-234177] [777.769]
[21.12.20.20:35:12] Min [464/468] [-1875000|-468516] [924.236]
[21.12.20.20:35:12] Max [564/561] [-1875000|-1874688] [1.918.448]
[21.12.20.20:35:12] Min [450/464] [-1875000|-1405662] [3.257.131]
[21.12.20.20:35:12] Max [569/564] [-1875000|-1170389] [5.677.585]
[21.12.20.20:35:12] Max [575/569] [-1875000|-700815] [10.923.967]
[21.12.20.20:35:12] Min [449/450] [-1875000|-640628] [269.409.791]
[21.12.20.20:35:12] Min [447/449] [-1875000|82120] [343.133.391]
[21.12.20.20:35:12] Max [576/575] [-1875000|-378142] [413.792.142]
[21.12.20.20:35:13] Min [445/447] [-1875000|-121064] [515.594.194]
[21.12.20.20:35:13] Max [578/576] [-1875000|-1055406] [588.187.774]
[21.12.20.20:35:14] Max [579/578] [-1875000|-982455] [996.807.888]
[21.12.20.20:35:15] Max [581/579] [-1875000|1624880] [1.791.911.611]
[21.12.20.20:35:15] Save [-1874999(5E-05%)] [03s 559n] [1.919.984.172]
[21.12.20.20:35:15] Min [441/445] [-1874999|-1636923] [1.934.725.577]
[21.12.20.20:35:19] Save [-1874998(0,00011%)] [03s 507n] [3.840.006.748]
[21.12.20.20:35:19] Min [437/441] [-1874998|-1837993] [4.027.758.271]
[21.12.20.20:35:19] Max [584/581] [-1874998|-1132272] [4.059.928.818]
[21.12.20.20:35:21] Max [585/584] [-1874998|1009530] [5.316.895.803]
[21.12.20.20:35:22] Save [-1874997(0,00016%)] [03s 498n] [5.760.010.912]
SetThreadAffinityMask - примерно бесполезная тема, как я и предполагал, избавьтесь от неё.
Ну и по мелочи подчистил код, где в глаза бросилось.
UPD: Увидел, в чем разница в поведении. Дело в том, что распараллеленный код находит при одинаковом количестве бедрока случайный чанк, а не первый по z. Поэтому z координата может отличаться от синхронного кода. Чтобы это исправить, можно накатить вот такой патч.
lock (_lock)
{
if (countBedrockInChunk > i.max.count)
{
Send(ConsoleColor.Green,
"Max " + "[" + countBedrockInChunk + "/" + i.max.count + "] [" + x + "|" + z
+ "] [" + (i.count + countBedrockInChunk).ToString("#,#", deDE) + "]");
i.max = new Point(x, z, countBedrockInChunk);
}
else if (countBedrockInChunk < i.min.count)
{
Send(ConsoleColor.Red,
"Min " + "[" + countBedrockInChunk + "/" + i.min.count + "] [" + x + "|" + z
+ "] [" + (i.count + countBedrockInChunk).ToString("#,#", deDE) + "]");
i.min = new Point(x, z, countBedrockInChunk);
}
else if (countBedrockInChunk == i.max.count && i.max.z > z)
{
i.max = new Point(x, z, countBedrockInChunk);
}
else if (countBedrockInChunk == i.min.count && i.min.z > z)
{
i.min = new Point(x, z, countBedrockInChunk);
}
}
Вот теперь результаты вывода в файл в точности совпадают с вашими.