Есть ли причина, по которой GDI+ будет работать медленнее в 64-битной операционной системе (или, может быть, это связано с Windows 7)

Фон

Я работаю над приложением, которое должно одинаково работать на каждой платформе Windows, начиная с XP. Благодаря платформе .NET это по большей части было очень просто. Приложение работает на различных сенсорных поверхностях. Приложение нацелено на .Net 3.0, но если по какой-то причине мне нужно перейти на .Net 3.5, я могу это сделать. Я не могу использовать 4.0.

Приложение активно использует GDI+ через пространство имен System.Drawing. Теперь я понимаю, что GDI+ либо вообще не имеет аппаратного ускорения, либо ускоряется только на очень небольшом количестве видеокарт, поэтому я ожидаю некоторых проблем с производительностью. Однако эта проблема является серьезной и значительно снижает удобство использования приложения. Я бы предпочел не переписывать все приложение целиком для DirectX или OpenGL, если этого можно избежать с помощью простого исправления.

Проблема

Недавно мы установили 64-разрядную версию Windows 7 на один из сенсорных столов. Это был первый раз, когда приложение запускалось на 64-битной машине с Windows 7. В любом случае, любое рисование (в частности, DrawLine) более чем одним пальцем на устройстве одновременно вызывает смехотворное отставание. Это отставание не проявляется в 32-битной Windows XP или 32-битной Windows 7, поэтому я думаю, что это может быть характерно для 64-битной Windows 7 (у меня нет 64-битной машины XP для тестирования).

Приложение также было вынуждено работать как 32-разрядное приложение из-за того, что один из файлов .dll имеет только 32-разрядные библиотеки, доступные для его компиляции. Я где-то читал, что перевод процесса в 32-битный режим в 64-битной системе может вызвать проблемы с производительностью, но когда я обновил SDK, который мы использовали, и создал 64-битную .dll и приложение, проблема осталась.

Я прочитал в этом другом потоке StackOverflow, что не должно быть различий между 32-битным и 64-битным приложением в отношении GDI+. Кажется, это не тот случай.

Итак, к чему это сводится: кто-нибудь из вас знает, почему может быть такая огромная разница в производительности между 32-битной и 64-битной Windows? Если я не смогу найти решение, мне придется потратить немного времени на то, чтобы все использовало аппаратное ускорение рисования. Я хотел бы избежать этого, если это вообще возможно.

Всем спасибо!

EDIT: запрошенный фрагмент кода

    public delegate void drawLineDelegate(Color color, float width, int x1, int y1, int x2, int y2);
    public void drawLine(Color color, float width, int x1, int y1, int x2, int y2)
    {
        if (InvokeRequired)
        {
            Invoke(new drawLineDelegate(drawLine), new object[] { color, width, x1, y1, x2, y2 });
        }
        else
        {
            try
            {
                lock (drawLock)
                {
                    pen.Width = width;
                    pen.Color = color;
                    scaledPen.Width = width * this.Width / Canvas.Width;
                    scaledPen.Color = color;
                    Point p1 = scalePointToScreen(new Point(x1, y1));
                    Point p2 = scalePointToScreen(new Point(x2, y2));

                    graphics.DrawLine(pen, x1, y1, x2, y2);
                    scaledGraphics.DrawLine(scaledPen, p1.X, p1.Y, p2.X, p2.Y);

                    this.Invalidate(new Rectangle(Math.Min(p1.X, p2.X) - (int)scaledPen.Width, Math.Min(p1.Y, p2.Y) - (int)scaledPen.Width,
                          Math.Abs(p2.X - p1.X) + 2 * (int)scaledPen.Width, Math.Abs(p2.Y - p1.Y) + 2 * (int)scaledPen.Width));
                }
            }
            catch (Exception ex)
            {
                if (Scribbler.Properties.Settings.Default.LogLevel >= 2)
                {
                    Scribbler.ScribblerForm.WriteLogMessage(ex.ToString());
                }
            }
        }

Правка: дополнительные исследования Я еще раз прошерстил Интернет в отношении карт NVidia, которые мы используем на наших машинах. Это привело меня к нескольким сообщениям на форумах разработчиков NVidia о людях с такой же проблемой. Распространенный ответ заключается в том, что GDI устарел в Windows 7, а более новые графические драйверы NVidia (начиная с версии 182.5) страдают от проблем с производительностью GDI. Решения заключались либо в обновлении до Direct3D, либо в использовании старых драйверов. Кто-нибудь знает наверняка, является ли это лучшим курсом действий?


person red_sky    schedule 21.07.2011    source источник
comment
WPF будет ускорен, что даст вам производительность DirectX без фактического вызова OpenGL или DirectX.   -  person Ben Voigt    schedule 22.07.2011
comment
Вы уверены, что проблема в GDI? Вы должны измерять тайминги. Обе системы Win 7 работали с включенным Aero?   -  person    schedule 22.07.2011
comment
И 32-битная Windows 7, и 64-битная Windows 7 работали с включенным Aero. Причина, по которой я думаю, что это связано с GDI+, заключается в том, что замедляется только само рисование. Графический интерфейс по-прежнему работает гладко. У нас была еще одна подобная проблема на той же таблице во всех версиях приложения, но принуждение приложения использовать только одно ядро ​​решило ее. Это было связано с тем, что GDI+ имеет внутреннюю блокировку, поэтому к нему может обращаться только один поток за раз. Одно ядро ​​облегчало это. Я думаю, может быть, новая проблема связана? И спасибо за совет по WPF, мне придется искать способы рисования так же, как gdi+.   -  person red_sky    schedule 22.07.2011
comment
Какое отношение имеет более чем один палец к чему-либо? Линии рисуются быстрее, если вы используете только один палец?   -  person Gabe    schedule 25.07.2011
comment
GDI не устарел и никогда не будет. GDI запускает так много Windows-приложений, что это было бы самоубийством для Microsoft, лишь крошечный процент коммерческих приложений будет использовать что-то кроме GDI.   -  person Erik Funkenbusch    schedule 25.07.2011
comment
@Gabe, код рисования работает очень быстро, пока вы не опустите более одного пальца (что рисует столько линий, сколько пальцев на устройстве). В этот момент он становится очень медленным.   -  person red_sky    schedule 26.07.2011
comment
@Mystere Man, я так и думал, но, похоже, это популярный ответ на форумах Nvidia.   -  person red_sky    schedule 26.07.2011
comment
@red_sky: То есть вы говорите, что медленное рисование нескольких строк одновременно?   -  person Gabe    schedule 26.07.2011
comment
Да, это медленная часть. Я предполагаю, что это как-то связано с тем фактом, что при обнаружении движения на капле (пальце) между текущим положением капли и ее последней позицией рисуется отрезок линии. Порог составляет что-то вроде 10 пикселей. Когда, например, на экране пять пальцев и они одновременно двигаются, количество операций рисования линий довольно велико.   -  person red_sky    schedule 26.07.2011
comment
@red_sky: Maperitive отображает карты с десятками тысяч линейных сегментов в режиме реального времени с линейным падением производительности. Ваши пальцы определенно не представляют проблемы для GDI+ — проблема в общей архитектуре и в том, как используется GDI+.   -  person Igor Brejc    schedule 26.07.2011
comment
@Igor Brejc: есть большая вероятность, что это та же проблема, что и раньше, с многопоточностью и отрисовкой GDI, хотя эта проблема была исправлена ​​(или, похоже, в другой конфигурации системы). Я просматривал источник, пытаясь понять, почему код выполняется в нескольких потоках, и единственное, что я обнаружил, это то, что удаленное взаимодействие использует несколько потоков под капотом. Одна вещь, о которой я подумал, это сделать весь рисунок в OnPaint, а там, где у нас есть существующий код рисования, вместо этого сделать недействительным элемент управления. Это плохая идея или это ничего не исправит?   -  person red_sky    schedule 26.07.2011
comment
@red_sky: Одна из основных проблем — огромный красный флаг. Проще говоря, кажется, что это приложение исправляли и переустанавливали так много раз, что никто больше не уверен, что и как оно делает. Проще говоря, система рисования должна быть переработана. Скорее всего, вы будете пытаться исправить это и заставить его работать. Возможно, вам повезет, и вы найдете какое-нибудь исправление, которое, кажется, работает... то есть до следующего изменения ОС, пакета обновлений или обновления оборудования. Кроме того, если вы стажер, этот уровень изменений, вероятно, выше вашего уровня навыков.   -  person Erik Funkenbusch    schedule 26.07.2011
comment
@red_sky: Чтобы подробнее остановиться на одной основной проблеме, это означает, что у вас где-то есть многопоточные взаимоблокировки, когда один фрагмент кода получает какой-то ресурс, который нужен другому потоку, а затем первому нужно что-то, что принадлежит другому потоку, поэтому они борются между собой. друг с другом. Многоядерные проблемы обычно являются очень серьезным признаком плохого дизайна.   -  person Erik Funkenbusch    schedule 26.07.2011
comment
@Mystere Man Я верю, что сам GDI + имеет внутреннюю блокировку, которая вполне может вызывать видимые замедления (из-за плохого дизайна с нашей стороны). Я поговорю с моим руководителем об этой проблеме и посмотрю, что она скажет по этому поводу. Спасибо за ответ.   -  person red_sky    schedule 26.07.2011


Ответы (2)


Я заметил, что вы блокируете свой код рисования. Вы рисуете из нескольких тем? Я подозреваю, что блокировка может иметь какое-то отношение к вашим проблемам с производительностью.

Вы не должны рисовать из нескольких потоков, если вы ТОЧНО не знаете, что делаете. В ОС слишком много ошибок, связанных с использованием многопоточной графики.

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

Я думаю, вам было бы очень полезно перепроектировать рабочий процесс рисования, чтобы он вызывался один раз и только из основного потока сообщений.

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

person Erik Funkenbusch    schedule 25.07.2011
comment
На самом деле я не могу объяснить, почему есть и блокировка, и вызов, или почему области становятся недействительными. Это было написано другим человеком до того, как я начал работать над проектом. Что касается проблемы с потоками, то в написанном нами коде нет ничего, что создавало бы фоновые потоки. Я подозреваю, что причина наличия нескольких потоков связана с нашей асинхронной настройкой сервера/клиента (с использованием .NET Remoting). - person red_sky; 26.07.2011
comment
@Mystere Man, я на 100% согласен с вашими комментариями. Этот код, кажется, нарушает множество правил в отношении использования GDI+, и тогда виноват GDI+. - person Igor Brejc; 26.07.2011
comment
@red_sky: клиент не должен напрямую вызывать отрисовку GDI+. Это не то, как предполагается использовать GDI+, и если вы попытаетесь форсировать его, он стонет и стонет. Вместо этого клиентские запросы должны отправляться в очередь и обрабатываться сервером партиями (или с помощью метода OnPaint). Кроме того, я не совсем понимаю, почему сервер должен выполнять рендеринг в реальном времени для клиентов. О каких клиентах идет речь? - person Igor Brejc; 26.07.2011
comment
Рендеринг не в реальном времени на стороне клиента. Действия отправляются на сервер частями, которые затем отправляются клиентам частями. Действия представляют собой массивы, содержащие команду для выполнения и координаты, связанные с действием. Затем клиент проходит через массив команд и выполняет их. Я могу заверить вас, что это не проблема производительности. Это только тогда, когда человек рисует несколькими пальцами на одном из сенсорных столов, как я упоминал в исходном посте. - person red_sky; 26.07.2011
comment
Если это так, то вы можете провести простой эксперимент: удалить весь код клиент-сервер, весь код потоков, весь код блокировки и т. д. и протестировать чистый рендеринг одним пальцем и несколькими пальцами. И запустите свой код через профайлер. Без профилирования любые заверения ничего не стоят и могут только ввести вас в заблуждение. - person Igor Brejc; 26.07.2011
comment
Это звучит как хорошая идея, по крайней мере, это поможет сузить, что именно вызывает проблему. Спасибо за ваше руководство до сих пор. Я буду честен, это моя первая стажировка, и это проект, который был довольно далеко, когда я начал работать над ним. Я еще не привык к профессиональной практике (с использованием профилировщиков), и я действительно предпочитаю писать весь свой собственный код, чем работать над чужим. - person red_sky; 26.07.2011

Прежде всего, я не уверен, что вы получите много преимуществ в производительности при использовании WPF. Если вы решите переписать свой графический код, я предлагаю перейти на Direct2D (вы все еще можете написать код на С#). Вот статья, в которой сравнивается производительность всех трех библиотек: http://www.teechart.net/reference/articles/WhitePaper_Direct2D.htm

Я разработчик Maperitive, который активно использует GDI+ для рендеринга карт. Я не могу сказать, что заметил какие-либо серьезные проблемы с производительностью на 64-битной Windows 7, но опять же, я не тестировал ее на какой-либо другой системе (кроме Ubuntu Mono на нетбуке, который по понятным причинам намного медленнее).

Я думаю, было бы легче обсудить это, если бы вы показали нам несколько фрагментов вашего кода отрисовки линий. С другой стороны, проблема может быть связана с аппаратным обеспечением (драйвер видеокарты?). На скольких различных 64-битных конфигурациях Win 7 вы это тестировали?

ОБНОВЛЕНИЕ:

Об устаревшем GDI в Windows 7: я очень сомневаюсь, что это будет правдой, потому что тогда вы заметите проблемы с производительностью не только в своем приложении, и я сомневаюсь, что Microsoft осмелится сделать что-то подобное из соображений обратной совместимости. У вас есть ссылки, подтверждающие это утверждение?

О вашем коде: вы не показали все это и его реальное назначение, но из контекста я подозреваю, что вы используете объект Graphics, созданный с использованием метода Control.CreateGraphics() (и вы также вызываете рисование линий из других потоков, что может быть проблемой производительности само по себе). Я подозреваю, что вы делаете это для интерактивного рисования линий (вместо того, чтобы перерисовывать все с помощью метода OnPaint()). В целом такая практика проблематична (вот объяснение Боба Пауэлла< /а>).

Кстати, в чем разница между graphics и scaledGraphics?

Если бы вы могли объяснить, чего именно вы пытаетесь достичь с помощью этого метода, возможно, мы сможем найти лучший (более быстрый) способ сделать это.

person Igor Brejc    schedule 23.07.2011
comment
Прошу прощения, что так долго не мог ответить на ваш пост. Я был без интернета на выходные. Проблема с переходом на Direct2D заключается в том, что более половины наших клиентов все еще используют XP. Мы все еще обсуждаем, будем ли мы использовать OpenGL или DirectX, если понадобится. На данный момент мы протестировали приложение только на одной 64-разрядной конфигурации Win7. Проблема в том, что это наиболее распространенная конфигурация наших клиентов, поэтому важно, чтобы она работала на этом столе. Что касается кода рисования, у нас есть настраиваемый элемент управления, который находится в основной форме. У нас есть его экземпляр, и мы вызываем его отрисовку - person red_sky; 25.07.2011
comment
функции, которые просто вызывают graphics.drawline. Тем не менее, я опубликую часть кода в исходном посте. - person red_sky; 25.07.2011
comment
Спасибо за ссылку на Боба Пауэлла; индекс все еще работает, и если вы замените .htm на .aspx в ссылках, они тоже будут работать. - person Mark Ransom; 26.07.2011
comment
Я был на сайте Боба Пауэлла несколько раз, но не помню, зачем именно. ScaledGraphics используется для уменьшения масштаба всего, что отправляется по сети, до разрешения клиента, а не для разрешения отправителя. графика также используется для создания изображений в реальном размере того, что было нарисовано (а также для хранения рисунков с исходным разрешением). Сами графические объекты создаются из изображения при обновлении фона (что случается не часто). Отрисовка линий не вызывается из других потоков намеренно, но в конечном итоге это происходит в результате - person red_sky; 26.07.2011
comment
в результате асинхронной настройки сервера/клиента. Сначала у нас были проблемы с производительностью из-за проблем с многопоточностью, пока мы не заставили приложение использовать только одно ядро. Казалось, это исправило это во всем, кроме 64-битной конфигурации Windows 7. - person red_sky; 26.07.2011
comment
Клиент? Сервер? Вот вы меня совсем запутали :). Я думаю, у вас много проблем, которые на самом деле не связаны напрямую с GDI+ и могут серьезно повлиять на производительность. Также: создание Graphics из образа - образа чего? Экран? Еще одна проблемная вещь с точки зрения производительности. - person Igor Brejc; 26.07.2011
comment
Графика создается из Bitmap, который создается из MemoryStream при вызове функции setBackground. Эти потоки памяти создаются из нескольких вызовов p/, которые создают растровое изображение из дескриптора рабочего стола и разрешения. - person red_sky; 26.07.2011
comment
@red_sky: как часто создается это растровое изображение? - person Igor Brejc; 26.07.2011
comment
Очень редко. Он создается один раз при запуске приложения и когда пользователь вручную обновляет изображение, чтобы получить новое фоновое изображение рабочего стола. Автообновления не происходит. - person red_sky; 26.07.2011