Производительность параллели управления диаграммой.NET

Я использую Библиотеку программ управления Диаграммы.NET, которая идет с.NET 4.0 Беты 2, чтобы создать и сохранить изображения на диск на фоновом потоке. Я не показываю диаграмму на экране, однако, просто составляя таблицу, сохраняя его на диск, и уничтожаю его. Что-то вроде этого:

public void GeneratePlot(IList<DataPoint> series, Stream outputStream) {
    using (var ch = new Chart()) {
        ch.ChartAreas.Add(new ChartArea());
        var s = new Series();
        foreach (var pnt in series) s.Points.Add(pnt);
        ch.Series.Add(s);
        ch.SaveImage(outputStream, ChartImageFormat.Png);
    }
}

Требовалось приблизительно 300 - 400 мс, чтобы создать и сохранить каждую диаграмму. У меня есть потенциально сотни диаграмм для создания, таким образом, я думал, что буду использовать Parallel.For() параллелизировать эти задачи. У меня есть 8 базовых машин, однако, когда я пытаюсь составить 4 таблицы за один раз, моя диаграмма создают/сохраняют увеличения времени к где угодно от 800 до 1 400 мс, почти весь из которого используется Chart.SaveImage.

Я думал, что это могло бы быть ограничением диска ввод-вывод, так для тестирования этого я изменил последнюю строку на:

ch.SaveImage(Stream.Null, ChartImageFormat.Png);

Даже при записи в пустой поток производительность все еще о том же (800 - 1400 мс).

Разве я, как предполагается, не создаю изображения на фоновых потоках параллельно с этой библиотекой, или я делаю что-то не так?

Спасибо

Править: Добавленный полный пример кода

Просто изменитесь, флаг передал CreateCharts() протестировать параллель по сравнению с сериалом.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms.DataVisualization.Charting;

namespace ConsoleChartTest
{
    class Program
    {
        public static void GeneratePlot(IEnumerable<DataPoint> series, Stream outputStream)
        {
            long beginTime = Environment.TickCount;

            using (var ch = new Chart())
            {
                ch.ChartAreas.Add(new ChartArea());
                var s = new Series();
                foreach (var pnt in series)
                    s.Points.Add(pnt);
                ch.Series.Add(s);

                long endTime = Environment.TickCount;
                long createTime = endTime - beginTime;

                beginTime = Environment.TickCount;
                ch.SaveImage(outputStream, ChartImageFormat.Png);
                endTime = Environment.TickCount;
                long saveTime = endTime - beginTime;

                Console.WriteLine("Thread Id: {0,2}  Create Time: {1,3}  Save Time: {2,3}",
                    Thread.CurrentThread.ManagedThreadId, createTime, saveTime);
            }
        }

        public static void CreateCharts(bool parallel)
        {
            var data = new DataPoint[20000];
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = new DataPoint(i, i);
            }

            if (parallel)
            {
                Parallel.For(0, 10, (i) => GeneratePlot(data, Stream.Null));
            }
            else
            {
                for (int i = 0; i < 10; i++)
                    GeneratePlot(data, Stream.Null);
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Main Thread Id: {0,2}", Thread.CurrentThread.ManagedThreadId);

            long beginTime = Environment.TickCount;
            CreateCharts(false);
            long endTime = Environment.TickCount;
            Console.WriteLine("Total Time: {0}", endTime - beginTime);
        }
    }
}
6
задан dewald 30 January 2010 в 20:35
поделиться

4 ответа

Возникли проблемы с пространством имен System.Drawing . Там есть тяжелая блокировка потоков, которая будет сериализовать определенные задачи. Пока вы не вызовете Chart.SaveImage () , он на самом деле визуализирует изображение, это то, что съедает все ваше время.

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

Поиграйте с count = 50 в основном методе здесь ... одновременный просмотр обоих выходов помогает, я думаю, вы можете видеть, что параллельный последовательно работает быстрее, хотя это не так » t линейно масштабируется из-за блокировки в пространстве имен чертежа:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms.DataVisualization.Charting;

namespace ConsoleChartTest
{
  class Program
  {
    static void Main(string[] args)
    {
      var count = 50;
      Console.WriteLine("Serial Test Start, Count: {0}");
      Console.WriteLine("Main Thread Id: {0,2}", Thread.CurrentThread.ManagedThreadId);

      var sw = new Stopwatch();
      sw.Start();
      CreateCharts(count, false);
      sw.Stop();
      Console.WriteLine("Total Serial Time: {0}ms", sw.ElapsedMilliseconds);

      Console.WriteLine("Parallel Test Start");
      Console.WriteLine("Main Thread Id: {0,2}", Thread.CurrentThread.ManagedThreadId);

      sw.Restart();
      CreateCharts(count, true);
      sw.Stop();
      Console.WriteLine("Total Parallel Time: {0}ms", sw.ElapsedMilliseconds);
    }

    public static void GeneratePlot(IEnumerable<DataPoint> series, Stream outputStream)
    {
      var sw = new Stopwatch();
      sw.Start();

        var ch = new Chart();
        ch.ChartAreas.Add(new ChartArea());
        var s = new Series();
        foreach(var pnt in series) s.Points.Add(pnt);
        ch.Series.Add(s);

        sw.Stop();
        long createTime = sw.ElapsedMilliseconds;
        sw.Restart();

        ch.SaveImage(outputStream, ChartImageFormat.Png);
        sw.Stop();

        Console.WriteLine("Thread Id: {0,2}  Create Time: {1,3}ms  Save Time: {2,3}ms",
            Thread.CurrentThread.ManagedThreadId, createTime, sw.ElapsedMilliseconds);
    }

    public static void CreateCharts(int count, bool parallel)
    {
      var data = new DataPoint[20000];
      if (parallel)
      {
        Parallel.For(0, data.Length, (i) => data[i] = new DataPoint(i, i));
        Parallel.For(0, count, (i) => GeneratePlot(data, Stream.Null));
      }
      else
      {
        for (int i = 0; i < data.Length; i++)
          data[i] = new DataPoint(i, i);
        for (int i = 0; i < count; i++)
          GeneratePlot(data, Stream.Null);
      }
    }
  }
}

Блокируется Chart.SaveImage () -> ChartImage.GetImage () -> ChartPicture. Paint ()

3
ответ дан 17 December 2019 в 18:16
поделиться

Может быть, вы могли бы сохранить изображение в качестве BMP, что потребовало бы больше дискового пространства, но сократилось бы на сумму обработки, которую нужно будет делать. Вы должны попробовать разные форматы изображения, чтобы увидеть, что сжатие вызывает проблемы. Если это так, вы можете вращать некоторые другие потоки, чтобы выполнить сжатие после.

0
ответ дан 17 December 2019 в 18:16
поделиться

Помните, у вас есть Hyper-threading и действительно ядра. Вы должны быть осторожны, гиперпоточность не имеет того же производительность в качестве ядра.

Другое дело, что при параллельном выполнении лучше всего работать, когда вы создаете поток пула, вы должны установить максимальное количество потоков, например

MaxThreads = Cores - 2;

Когда я говорю "ядра", я говорю "ядра", а не ядра с гиперпоточностью.

1 - ОС 1 - Основное приложение X - Ядра для обработки.

Если вы создадите слишком много потоков, вы потеряете производительность из-за одновременного использования процессора.

Создание изображений в формате Jpeg или PNG - еще один хороший момент, так вы будете меньше времени на HD при сохранении изображения. Также позаботьтесь о качестве JPEG и PNG, потому что если оно 100 % может быть большим.

Другой важный момент. У вас будет согласие на HD, потому что будет много потоков, создающих на нем архивы. Что вы можете с этим поделать? Это действительно проблема, которую сложнее решить, потому что у нас нет параллельных жестких дисков. Таким образом, вы можете создать место для отправки буферов изображений, как и другой поток, который ничего не обрабатывает, просто там, например, принимает буфер изображений и сохраняет его во внутреннем списке. И через 5 из 5 секунд (или при некоторых условиях, которые вы думаете, что это лучше) он начинает записывать картинки на hd.Таким образом, у вас будет поток, просто работающий на HD «без» согласования, а другие потоки - только для обработки изображений.

at.

0
ответ дан 17 December 2019 в 18:16
поделиться

Если бы я должен был догадаться, то сказал бы, что код внутри SaveImage защищен CriticalSection или Lock, что позволяет запускать только один поток за раз по частям этого кода.

Таким образом, вы можете быть правы насчет того, что вам не разрешается использовать фоновые потоки, но я думаю, что более вероятно, что вам не разрешается иметь более 1 потока, запущенного внутри SaveImage за раз. Документация по этой функции довольно скудная, но тайминги очень наводят на размышления. 4 графика занимают примерно в 4 раза большую длину, чем 1 график.

Если сохранить всего 1 график, используя фоновый поток - работает ли он на полной скорости?

.
0
ответ дан 17 December 2019 в 18:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: