Оптимизация производительности C#.NET (Алгоритм, Многопоточность, Debug, Release, .Net Core, Net Native)
Формулировка задачи:
Решил поделится своим небольшим опытом по оптимизации вычислений на C#.NET.
НЕ профи, палками не кидать, конструктив приветствуется!
Тестом будет служить время вычисление всех переменных в заданном диапазоне (до 100000) в уравнении x^3 + y^3 = z^3 - 1
Симметричные решения по x и y не учитываем, т.е. из вариантов х=6,у=8,z=9 и х=8,у=6,z=9 - берем один (любой).
Оборудование/Софт:
ШАГ1. Все кажется просто, берем и перебором ищем значения. Здесь сражу же упираемся в большое количество итераций - время выполнение неприлично большое.
Поэтому ШАГ2 - Оптимизируем алгоритм, создаем массив из степеней Z, переберем значения х, у, обрезаем ненужные итерации, оставляем только один вариант x,y и обрезаем итерации на суммах x^3 + y^3 > z^3. Здесь (код) пальма первенства справедливо принадлежит
Время выполнения чуть более 4 минут (по ссылке выше есть так же расчет и на Freebasic). Пока прохладно.
ШАГ4. В C# (как проверено и не только в C#) расчет корня 3 степени медленнее, чем прямой перебор в массиве с некоторой хитростью (т.к. он отсортирован и перебор всегда ведется от меньшего к большему, или наоборот, легко запоминать последнюю найденную позицию и искать начиная с неё в след. цикле). Эта мысль меня посетила и была сразу реализована. Код (Сборка Debug x64) - делю на 4 потока, т.к. больше для моего ЦП -нет смысла:
Время выполнения - 41 сек. Уже теплее.
ШАГ5. Как же я забыл про Release, срочно исправляюсь - Сборка Release x64.
За счет оптимизации при компилировании получаем время выполнения - 17 секунд. Уже тепло.
Появляются мысли про оптимизацию цикла (в сети множество советов и примеров), т.к. для вычисления требуется несколько миллиардов итераций. Думаю.
ШАГ6. Почитав про .Net Core, решил попробовать.
Новый проект - Консольное приложение .Net Core - вставляем предыдущий код и ...
Время выполнения - 10,5 сек.
Здесь мне кажется, что сделан максимум на C#.
ШАГ7. Решил добить тему и погуглить.
Есть интересный зверь net native, подробно писать здесь не буду. Кратко: В Windows 10 универсальные Windows-приложения на управляемых языках (C#, VB) проходят в магазине процедуру компиляции в машинный код с использованием .NET Native.
Поэтому: создать проект-пустое приложение (универсальное приложение Windows)(UWP).
Создаем button, textBox (на кнопку код, в textBox - переменные).
Здесь нас поджидает проблемы с Threading.
Переходим на Task, двигаем дальше.
Код (Сборка Release x64)
Время выполнения 7,5 сек.
Без вывода строки 5 сек.(подумать со строкой)
ШАГ8. АССЕМБЛЕР... здесь мои знания заканчиваются...
Что скажут ОТЦЫ, есть ли дальнейшая оптимизация?
В блоге.
Тип ЦП QuadCore AMD Phenom II X4 Black Edition 955, 3200 MHz (16 x 200)
Системная плата Asus M4A89GTD Pro (2 PCI, 1 PCI-E x1, 1 PCI-E x4, 2 PCI-E x16, 4 DDR3 DIMM, Audio, Video, Gigabit LAN, IEEE-1394)
Чипсет системной платы AMD 890GX, AMD K10
Системная память 8192 МБ (DDR3-1333 DDR3 SDRAM)
Софт Win10x64, Excel2016x64, Visual Studio 2017
m-ch
- опять неприлично долго в VBA более 10 мин. ШАГ3. Берем этот алгоритм, переносим на C# и делим потоки, благо это задача хорошо бьется по потокам. Код (Сборка Debug x64):using System; using System.Collections.Generic; using System.Threading; //поиск переменных для решения уравнения x^3 + y^3 = z^3 - 1 class Program { static long n; static long k = 0; static long[] a; static void Main(string[] args) { var threads = new List<Thread>(); for (int i = 2; i <= 9; i++) { int k = i; threads.Add(new Thread(() => func(k))); } Console.Write("Задайте диапазон расчета переменных, до..., нажмите Enter: "); n = Convert.ToInt64(Console.ReadLine()); a = new long[n + 1]; DateTime dold = DateTime.Now; for (long i = 1; i <= n; i++) { a[i] = (long)Math.Pow(i, 3); } threads.ForEach(t => t.Start()); threads.ForEach(t => t.Join()); //ждем выполнения всех потоков TimeSpan sp = DateTime.Now - dold; Console.WriteLine(sp); Console.Write("Программа завершена. Нажмите Enter для выхода..."); Console.ReadLine(); } static void func(long start) { // здесь код, который будет выполняться в отдельном потоке for (long x = start; x <= (long)Math.Pow(((a[n]) - 1) / 2, 1.0 / 3.0); x = x + 8)//на 8 потоков { for (long y = x; y <= (long)Math.Pow(((a[n]) - 1 - a[x]), 1.0 / 3.0); y++) { long z3 = a[x] + a[y] + 1; long z = (long)(Math.Pow(z3, 1.0 / 3.0) + 0.5); if (a[z] == z3) { k = ++k; Console.WriteLine(k + " " + x + " " + y + " " + z); } } } } }
using System; using System.Collections.Generic; using System.Threading; using System.Diagnostics; namespace ConsoleApplication1 { //поиск переменных для решения уравнения x^3 + y^3 = z^3 - 1 class Program { static long n; static long k = 0; static long[] a; static long threadsN = 4; //задать 4 потоков static void Main(string[] args) { var threads = new List<Thread>(); for (int i = 1; i <= threadsN; i++) { int k = i; threads.Add(new Thread(() => func(k))); } Console.Write("Задайте диапазон расчета переменных, до..., нажмите Enter: "); n = Convert.ToInt64(Console.ReadLine()); a = new long[n + 1]; //DateTime dold = DateTime.Now; Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); for (long i = 1; i <= n; i++) a[i] = (i * i * i); // Math.Pow не использовал из-за ошибок округления threads.ForEach(t => t.Start());//стартуем потоки threads.ForEach(t => t.Join()); //ждем выполнения всех потоков sw.Stop(); //Console.WriteLine(sp); Console.WriteLine("Время вычисления: {0}", sw.Elapsed); Console.Write("Программа завершена. Нажмите Enter для выхода..."); Console.ReadLine(); } static void func(long start) { // здесь код, который будет выполняться в отдельном потоке long m = n; for (long x = start; x <= (long)Math.Pow(((a[n]) - 1) / 2, 1.0 / 3.0); x = x + threadsN)//на 4 потоков { long s = 1; while (a[m] > (a[n]) - 1 - a[x]) m--; for (long y = x; y <= m; y++) { long z3 = a[x] + a[y] + 1; while ((a[s]) <= z3) { if (a[s] == z3) { k++; Console.WriteLine(k + " " + x + " " + y + " " + s); } s++; } } } } } }
using System; using System.Collections.Generic; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using System.Threading.Tasks; using System.Diagnostics; namespace App1 { public sealed partial class MainPage : Page { static long n; static long k = 0; static long[] a; static string str = ""; static long TaskN = 4; //задать 4 task public MainPage() { this.InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { n = 100000; a = new long[n + 1]; Stopwatch sw = Stopwatch.StartNew(); for (long i = 1; i <= n; i++) a[i] = (i * i * i); // Math.Pow не использовал из-за ошибок округления var task = new List<Task>(); for (int i = 1; i <= TaskN; i++) { int k = i; task.Add(new Task(() => func(k))); } task.ForEach(t => t.Start());//стартуем task task.ForEach(t => t.Wait()); //ждем выполнения всех task sw.Stop(); button.Content = sw.Elapsed.TotalSeconds; textBox.Text = str; } static void func(long start) { // здесь код, который будет выполняться в отдельном task long m = n; for (long x = start; x <= (long)Math.Pow(((a[n]) - 1) / 2, 1.0 / 3.0); x=x+TaskN) //на 4 task { long s = 1; while (a[m] > (a[n]) - 1 - a[x]) m--; for (long y = x; y <= m; y++) { long z3 = a[x] + a[y] + 1; while ((a[s]) <= z3) { if (a[s] == z3) { k++; str = str + Environment.NewLine + (k + " " + x + " " + y + " " + s); } s++; } } } } } }
Решение задачи: «Оптимизация производительности C#.NET (Алгоритм, Многопоточность, Debug, Release, .Net Core, Net Native)»
textual
Листинг программы
z = Int(x * 1.2599)
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д