Моя 32-битная головная боль теперь превратилась в 64-битную мигрень?!?(или проблемы с 64-битной средой выполнения .NET CLR)

StackOverflow https://stackoverflow.com/questions/635019

  •  10-07-2019
  •  | 
  •  

Вопрос

Какие необычные и неожиданные последствия произошли с точки зрения производительности, памяти и т. д. при переходе от запуска ваших .NET-приложений под 64-битной JIT по сравнению с 64-разрядной JIT-версией?32-битный JIT?Меня интересует хорошее, но больше меня интересуют удивительно плохие проблемы, с которыми сталкиваются люди.

Я пишу новое приложение .NET, которое будет развернуто как в 32-битной, так и в 64-битной версиях.Было много вопросов, касающихся проблем с портированием приложения - меня не волнует вопрос «ошибки» с точки зрения программирования/портирования.(т.е.:Правильная обработка встроенного/COM-взаимодействия, ссылочные типы, встроенные в структуры, изменение размера структуры и т. д.)

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

Было много вопросов и сообщений в блогах, которые обходили бы эту проблему или затрагивали один ее аспект, но я не видел ничего, что составило бы достойный список проблем.

В частности, мое приложение сильно привязано к процессору и имеет огромные шаблоны использования памяти (следовательно, в первую очередь, нужна 64-битная версия), а также имеет графический характер.Меня беспокоят другие скрытые проблемы, которые могут существовать в среде CLR или JIT, работающей в 64-разрядной версии Windows (с использованием .NET 3.5sp1).

Вот несколько проблем, о которых мне известно в настоящее время:

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

Спасибо вам всем!

----РЕДАКТИРОВАТЬ-----

Просто для уточнения -

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

Однако мое приложение не является типичным бизнес-приложением.Это научное программное обеспечение.У нас есть много процессов, которые используют 100% ЦП на всех ядрах (они многопоточны) в течение нескольких часов.

Я трачу МНОГО времени на профилирование приложения, и это имеет огромное значение.Однако большинство профилировщиков отключают многие функции JIT, поэтому мелкие детали в таких вещах, как распределение памяти, встраивание в JIT и т. д., может быть очень сложно определить, когда вы работаете под профилировщиком.Отсюда моя потребность в этом вопросе.

Это было полезно?

Решение

Я помню, как услышал о проблеме на канале IRC, который часто посещаю.В этом случае он оптимизирует временную копию:

EventHandler temp = SomeEvent;
if(temp != null)
{
    temp(this, EventArgs.Empty);
}

Возвращение состояния гонки и возникновение потенциальных исключений нулевых ссылок.

Другие советы

Особенно неприятная проблема с производительностью в .NET связана с плохим JIT:

https://connect.microsoft.com/VisualStudio/feedback/details/93858/struct-methods-should-be-inlined?wa=wsignin1.0

По сути, встраивание и структуры не очень хорошо работают вместе на x64 (хотя эта страница предлагает встраивание теперь работает, но последующие избыточные копии не удаляются, что звучит подозрительно, учитывая крошечную производительность.разница).

В любом случае, после достаточно долгой борьбы с .NET, мое решение состоит в том, чтобы использовать C++ для чего-либо, требующего больших вычислений.Даже в «хороших» случаях для .NET, когда вы не имеете дело со структурами и используете массивы, где оптимизирована проверка границ, C++ превосходит .NET. руки вниз.

Если вы делаете что-то более сложное, чем скалярное произведение, картина очень быстро ухудшается;код .NET одновременно длиннее и менее читабелен (потому что вам нужно вручную встраивать данные и/или не можете использовать дженерики), и намного медленнее.

Я перешел на использование Эйген в С++:это просто великолепно, благодаря чему получается читаемый код и высокая производительность;затем тонкая оболочка C++/CLI обеспечивает связующее звено между вычислительным механизмом и миром .NET.

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

Так, например, если P это матрица, такие вещи просто работают:

1.0 /  (P.transpose() * P).diagonal().sum();

... который не выделяет временно транспонированный вариант P и не вычисляет весь матричный продукт, а только те поля, которые ему нужны.

Итак, если вы можете работать в режиме полного доверия - просто используйте C++ через C++/CLI, это работает намного лучше.

В большинстве случаев Visual Studio и компилятор неплохо скрывают от вас проблемы.Однако я знаю об одной серьезной проблеме, которая может возникнуть, если вы настроите свое приложение на автоматическое определение платформы (x86 или x64). а также есть какие-либо зависимости от 32-битных сторонних DLL.В этом случае на 64-битных платформах он попытается вызвать библиотеки DLL, используя 64-битные соглашения и структуры, но это просто не сработает.

Вы упомянули о проблемах портирования, это то, о чем стоит беспокоиться.Я (очевидно) не знаю вашего приложения, но попытки угадать JIT часто являются пустой тратой времени.Люди, которые пишут JIT, имеют глубокое понимание архитектуры чипов x86/x64 и, по всей вероятности, знают, что работает лучше, а что хуже, чем, вероятно, кто-либо другой на планете.

Да, возможно, у вас есть необычный и уникальный случай, но если вы «находитесь в процессе написания новый application", то я бы не беспокоился о JIT-компиляторе.Вероятно, где-то существует глупый цикл, которого можно избежать, и который даст вам 100-кратное повышение производительности, которое вы получите, пытаясь угадать JIT.Напоминает мне о проблемах, с которыми мы столкнулись при написании ORM: мы смотрели на код и думали, что могли бы подкорректировать из него пару машинных инструкций...конечно, затем код отключился и подключился к серверу базы данных по сети, поэтому мы сократили микросекунды процесса, который где-то в другом месте был ограничен миллисекундами.

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

Об ответе Quibblesome:

Я попытался запустить следующий код в своей Windows 7 x64 в режиме выпуска без отладчика и NullReferenceException никогда не бросали.

using System;
using System.Threading;

namespace EventsMultithreadingTest
{
    public class Program
    {
        private static Action<object> _delegate = new Action<object>(Program_Event);
        public static event Action<object> Event;

        public static void Main(string[] args)
        {
            Thread thread = new Thread(delegate()
                {
                    while (true)
                    {
                        Action<object> ev = Event;

                        if (ev != null)
                        {
                            ev.Invoke(null);
                        }
                    }
                });
            thread.Start();

            while (true)
            {
                Event += _delegate;
                Event -= _delegate;
            }
        }

        static void Program_Event(object obj)
        {
            object.Equals(null, null);
        }
    }
}

Я считаю, что 64 JIT не полностью разработан/перенесен для использования преимуществ таких процессоров с 64-битной архитектурой, поэтому у него есть проблемы: вы можете получить «эмулируемое» поведение ваших сборок, что может вызвать проблемы и неожиданное поведение.Я бы рассмотрел случаи, когда этого можно избежать, и/или, возможно, посмотрел, есть ли хороший быстрый компилятор 64 C++ для написания критичных по времени вычислений и алгоритмов.Но даже если у вас возникли трудности с поиском информации или у вас нет времени читать разобранный код, я совершенно уверен, что вынесение тяжелых вычислений за пределы управляемого кода уменьшит любые проблемы, которые могут у вас возникнуть, и повысит производительность [отчасти уверен, что вы уже делаете это но просто упомянуть:)]

Профилировщик не должен существенно влиять на ваши результаты синхронизации.Если накладные расходы профилировщика действительно есть «значительно», то вы, вероятно, не сможете выжать из своего кода гораздо больше скорости, и вам следует подумать о том, чтобы проверить узкие места вашего оборудования (диск, ОЗУ или ЦП?) и обновить его.(Похоже, вы ограничены в процессоре, так что вот с чего начать)

В общем, .net и JIT освобождают вас от большинства проблем портирования 64-битной версии.Как вы знаете, существуют эффекты, связанные с размером регистра (изменения использования памяти, маршалинг в машинный код, необходимость создания всех частей программы в нативных 64-битных сборках) и некоторые различия в производительности (большая карта памяти, больше регистров, более широкие шины). и т. д.), поэтому я не могу сказать вам ничего большего, чем вы уже знаете на эту тему.Другие проблемы, с которыми я столкнулся, связаны с ОС, а не с C# - например, теперь существуют разные кусты реестра для 64-разрядных приложений и приложений WOW64, поэтому некоторые обращения к реестру необходимо прописывать осторожно.

Как правило, это плохая идея беспокоиться о том, что JIT будет делать с вашим кодом, и пытаться настроить его для лучшей работы, потому что JIT, скорее всего, изменится с .net 4, 5 или 6, и ваши «оптимизации» могут обернуться неэффективностью. или, что еще хуже, ошибки.Также имейте в виду, что JIT компилирует код специально для процессора, на котором он работает, поэтому потенциально улучшение на вашем ПК для разработки может не быть улучшением на другом ПК.То, что вам сходит с рук при использовании сегодняшнего JIT на современном процессоре, может укусить вас через годы, когда вы что-то обновите.

В частности, вы цитируете «свойства не встроены в x64».К тому времени, как вы проработаете всю свою кодовую базу, превратив все свои свойства в поля, вполне может появиться новый JIT для 64-разрядной версии, который выполняет встроенные свойства.Действительно, он вполне может работать лучше, чем ваш «обходной» код.Позвольте Microsoft оптимизировать это для вас.

Вы справедливо отмечаете, что профиль вашей памяти может измениться.Поэтому вам может потребоваться больше оперативной памяти, более быстрые диски для виртуальной памяти и больший кэш ЦП.Все аппаратные проблемы.Возможно, вы сможете уменьшить эффект, используя (например) Int32, а не int, но это может не иметь большого значения и потенциально может снизить производительность (поскольку ваш процессор может обрабатывать собственные 64-битные значения более эффективно, чем 32-битные значения половинного размера). ).

Вы говорите, что «время запуска может быть больше», но это кажется совершенно неуместным в приложении, которое, по вашим словам, работает часы при 100% загрузке процессора.

Так что же тебя действительно беспокоит?Возможно, вы замерите время своего кода на 32-битном ПК, а затем засеките время выполнения той же задачи на 64-битном ПК.Есть ли разница в полчаса по сравнению с 4-часовой пробежкой?Или разница всего 3 секунды?Или 64-битный ПК на самом деле быстрее?Возможно, вы ищете решения несуществующих проблем.

Итак, вернемся к обычному, более общему совету.Профиль и время для выявления узких мест.Посмотрите на алгоритмы и математические процессы, которые вы применяете, и попытайтесь улучшить/заменить их более эффективными.Убедитесь, что ваш многопоточный подход помогает, а не вредит вашей производительности (т.что ожидания и блокировки избегаются).Попробуйте уменьшить выделение/освобождение памяти - например.повторно использовать объекты, а не заменять их новыми.Постарайтесь сократить использование частых вызовов функций и виртуальных функций.Перейдите на C++ и избавьтесь от накладных расходов, связанных со сборкой мусора, проверкой границ и т. д.что .net навязывает.Хм.Ничего из этого не имеет ничего общего с 64-битной версией, не так ли?

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

Мы должны забыть о небольшой эффективности, скажем, около 97% случаев:Превосходная оптимизация - это корень всего зла.-- Дональд Кнут

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top