Рассмотрим использование ViewModelLocator. Вместо привязки datacontext к ViewModel в вашем случае выше вы привязываетесь к локатору, который знает, как разрешить ViewModel из контейнера (единства) и в процессе вложить любые зависимости в конструктор.
Я полагаю, что EventAggregator зарегистрирован в контейнере по умолчанию, поэтому он опубликовал запись в блоге John Papa и Glenn Block (один из людей за призмой). должен быть автоматически подключен к VM при разрешении VM из контейнера.
Следует упомянуть, что код из выше блога использует MEF. Этот блог , я считаю, имеет пример кода с использованием единицы
Хорошо, вы определяете проблему там, где, казалось бы, нет места для улучшения. По моему опыту, такое случается довольно редко. Я попытался объяснить это в статье доктора Доббса в ноябре 1993 года, начав с хорошо спроектированной нетривиальной программы без очевидных потерь и проведя ее через серию оптимизаций, пока время ее настенных часов не сократилось с 48 секунд. до 1,1 секунды, а размер исходного кода был уменьшен в 4 раза. Моим диагностическим инструментом был этот . Последовательность изменений была следующей:
Первой обнаруженной проблемой было использование кластеров списков (теперь называемых «итераторами» и «контейнерными классами»), которые составляли более половины времени. Они были заменены довольно простым кодом, в результате чего время сократилось до 20 секунд.
Сейчас больше всего времени уделяется составлению списков. В процентном отношении раньше он был не таким уж большим, но теперь это связано с тем, что более серьезная проблема была устранена. Я нахожу способ ускорить это, и время сокращается до 17 секунд.
Теперь труднее найти очевидных виновников, но есть несколько более мелких, с которыми я могу что-то сделать, и время уменьшается до 13 секунд.
Теперь я, кажется, наткнулся на стену. Образцы говорят мне, что именно он делает, но я не могу найти ничего, что можно было бы улучшить. Затем я размышляю над основным дизайном программы, над ее структурой, управляемой транзакциями, и спрашиваю, действительно ли весь выполняемый ею поиск по спискам обусловлен требованиями задачи.
дизайн, при котором программный код фактически генерируется (через макросы препроцессора) из меньшего набора источников, и в котором программа не постоянно выясняет то, что программист знает, что это довольно предсказуемо. Другими словами, не «интерпретируйте» последовательность действий, не «компилируйте» ее.
Дополнительная диагностика показывает, что он проводит время в управление очередью. Их встраивание сокращает время до 7 секунд.
Сейчас много времени занимает диагностическая печать, которую я делал. Сбросьте это - 4 секунды.
Сейчас больше всего времени тратят на звонки на malloc и free . Перерабатывать объекты - 2,6 секунды.
Продолжая выборку, я все еще нахожу операции, которые не являются строго необходимыми - 1,1 секунды.
Общий коэффициент ускорения: 43,6
Сейчас нет двух одинаковых программ, но в программном обеспечении, отличном от игрушечного, я всегда видел прогресс, как это. Сначала вы получаете легкие вещи, а затем более сложные, пока не дойдете до точки убывающей отдачи. Тогда полученное вами понимание вполне может привести к редизайну, началу нового раунда ускорений, пока вы снова не достигнете убывающей отдачи. Теперь это тот момент, когда имеет смысл задаться вопросом, ++ i
или i ++
или для (;;)
или while (1 )
быстрее: вопросы, которые я так часто вижу в Stack Overflow.
PS Может возникнуть вопрос, почему я не использовал профилировщик. Ответ заключается в том, что почти каждая из этих «проблем» был сайтом вызова функций, который точно определяет образцы стека. Профилировщики даже сегодня едва ли приходят к мысли, что операторы и инструкции вызова более важны для поиска и легче исправлять, чем целые функции.
Я на самом деле построил профилировщик для этого, но для настоящего упущения -и-грязная близость к тому, что делает код, ничто не заменит правильного понимания этого. То, что количество образцов мало, не является проблемой, потому что ни одна из обнаруженных проблем не настолько крошечная, что их легко упустить.
ДОБАВЛЕНО: jerryjvl запросил несколько примеров. Вот первая проблема. Он состоит из небольшого числа отдельных строк кода, которые вместе занимают половину времени:
/* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */
if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){
. . .
/* FOR EACH OPERATION REQUEST */
for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){
. . .
/* GET CURRENT TASK */
ptask = ILST_NTH(ptop->tasklist, ptop->current_task)
Они использовали кластер списков ILST (аналогично классу списков). Реализованы они обычным образом, с «скрытием информации». Это означает, что пользователи класса не должны были заботиться о том, как они были реализованы. Когда были написаны эти строки (из примерно 800 строк кода), не приходило в голову мысль, что они могут быть «узким местом» (ненавижу это слово). Это просто рекомендуемый способ делать что-то. Оглядываясь назад, легко сказать , , что этого следовало избегать, но по моему опыту все проблемы с производительностью похожи на эти. В общем, стараться избегать проблем с производительностью - это хорошо. Еще лучше найти и исправить те, которые созданы, даже если их «следовало избегать» (задним числом). Я надеюсь, что это придаст некоторый смысл.
Вот вторая проблема, в двух отдельных строках:
/* ADD TASK TO TASK LIST */
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)
Это построение списков путем добавления элементов к их концам. (Исправление заключалось в том, чтобы собрать элементы в массивы и построить все списки одновременно.) Интересно то, что эти операторы стоили (т.е. находились в стеке вызовов) только 3/48 исходного времени, поэтому их не было в на самом деле большая проблема в начале . Однако после устранения первой проблемы они стоили 3/20% времени и поэтому стали «более крупной рыбой». В общем, так оно и есть.
Я мог бы добавить, что этот проект произошел от реального проекта, над которым я помогал. В этом проекте проблемы с производительностью были гораздо более серьезными (как и ускорение), например, вызов подпрограммы доступа к базе данных во внутреннем цикле, чтобы узнать, завершена ли задача.
ССЫЛКА ДОБАВЛЕНА: находились в стеке вызовов) 3/48 от первоначального времени, так что они фактически не были большой проблемой в начале . Однако после устранения первой проблемы они стоили 3/20% времени и поэтому стали «более крупной рыбой». В общем, так оно и есть.
Я мог бы добавить, что этот проект произошел от реального проекта, над которым я помогал. В этом проекте проблемы с производительностью были гораздо более серьезными (как и ускорение), например, вызов подпрограммы доступа к базе данных во внутреннем цикле, чтобы узнать, завершена ли задача.
ССЫЛКА ДОБАВЛЕНА: находились в стеке вызовов) 3/48 от первоначального времени, так что они фактически не были большой проблемой в начале . Однако после устранения первой проблемы они стоили 3/20% времени и поэтому стали «более крупной рыбой». В общем, так оно и есть.
Я мог бы добавить, что этот проект произошел от реального проекта, над которым я помогал. В этом проекте проблемы с производительностью были гораздо более серьезными (как и ускорение), например, вызов подпрограммы доступа к базе данных во внутреннем цикле, чтобы узнать, завершена ли задача.
ССЫЛКА ДОБАВЛЕНА:
Я мог бы добавить, что этот проект произошел от реального проекта, в котором я помогал. В этом проекте проблемы с производительностью были гораздо более серьезными (как и ускорение), например, вызов подпрограммы доступа к базе данных во внутреннем цикле, чтобы узнать, завершена ли задача.
ССЫЛКА ДОБАВЛЕНА:
Я мог бы добавить, что этот проект произошел от реального проекта, в котором я помогал. В этом проекте проблемы с производительностью были гораздо более серьезными (как и ускорение), например, вызов подпрограммы доступа к базе данных во внутреннем цикле, чтобы узнать, завершена ли задача.
ССЫЛКА ДОБАВЛЕНА: Исходный код, как оригинальный, так и переработанный, можно найти на www.ddj.com , за 1993 год, в файле 9311.zip, файлах slug.asc и slug.zip.
EDIT 2011/11 / 26: Теперь существует проект SourceForge , содержащий исходный код на Visual C ++ и подробное описание того, как он был настроен. Он проходит только первую половину описанного выше сценария и не следует точно такой же последовательности, но все же получает ускорение на 2-3 порядка.
Когда вы больше не можете улучшить производительность - посмотрите, можно ли вместо этого улучшить воспринимаемое качество .
Возможно, вам не удастся ускорить выполнение алгоритма fooCalc, но часто есть способы заставить приложение выглядеть более отзывчивым для пользователя.
Несколько примеров:
Это не сделает вашу программу быстрее, но она может сделать ваших пользователей счастливее с вашей скоростью .
Сказать невозможно. Это зависит от того, как выглядит код. Если мы можем предположить, что код уже существует, то мы можем просто взглянуть на него и выяснить, как его оптимизировать.
Лучшая локализация кеша, развертывание цикла, Попытка устранить длинные цепочки зависимостей, чтобы получить более качественные инструкции - уровень параллелизма. По возможности предпочитайте условные переходы веткам. По возможности используйте инструкции SIMD.
Поймите, что делает ваш код, и поймите оборудование, на котором он работает. Тогда становится довольно просто определить, что вам нужно сделать для повышения производительности вашего кода. На самом деле это единственный действительно общий совет, который я могу придумать.
Ну, и «Покажите код на SO и попросите совета по оптимизации для этого конкретного фрагмента кода».
Иногда может помочь изменение компоновки ваших данных. В C вы можете переключиться с массива или структур на структуру массивов или наоборот.
Если лучшее оборудование - вариант, то определенно сделайте это. В противном случае
Очень сложно дать общий ответ на этот вопрос. Это действительно зависит от вашей проблемной области и технической реализации. Общая техника, которая практически не зависит от языка: выявление горячих точек кода, которые нельзя устранить, и ручная оптимизация кода ассемблера.
Вам, вероятно, следует рассмотреть «перспективу Google», то есть определить, как ваше приложение может стать в значительной степени распараллеленным и параллельным, что неизбежно в какой-то момент также будет означать, что вам нужно будет рассмотреть возможность распределения вашего приложения на разных машинах и сети, так что он может идеально масштабироваться почти линейно с оборудованием, которое вы ему используете.
С другой стороны, люди из Google также известны тем, что бросают много рабочей силы и ресурсов на решение некоторых проблем в проектах, инструментах и инфраструктуру, которую они используют, такую как, например, оптимизация всей программы для gcc с помощью специальной группы инженеров, взламывающих внутренние компоненты gcc, чтобы подготовить ее к типичным сценариям использования Google.
Аналогичным образом,Профилирование приложения больше не означает простое профилирование программного кода, но также всех окружающих его систем и инфраструктуры (например, сетей, коммутаторов, серверов, RAID-массивов) с целью выявления избыточности и потенциала оптимизации с точки зрения системы.
Я думаю, это уже было сказано по-другому. Но когда вы имеете дело с алгоритмом, интенсивно использующим процессор, вам следует упростить все внутри самого внутреннего цикла за счет всего остального.
Некоторым это может показаться очевидным, но я стараюсь сосредоточиться на этом независимо от языка, с которым работаю. Например, если вы имеете дело с вложенными циклами и находите возможность понизить уровень кода, в некоторых случаях вы можете значительно ускорить свой код. В качестве другого примера следует подумать о таких мелочах, как работа с целыми числами вместо переменных с плавающей запятой, когда это возможно, и использование умножения вместо деления, когда это возможно. Опять же, это вещи, которые следует учитывать для вашего самого внутреннего цикла.
Иногда может оказаться полезным выполнение математических операций с целым числом внутри внутреннего цикла, а затем его масштабирование до переменной с плавающей запятой, с которой впоследствии можно будет работать. Это пример того, как жертвовать скоростью в одном разделе ради улучшения скорости в другом, но в некоторых случаях это того стоит.
Предложения:
Я провожу большую часть своей жизни именно в этом месте. Общие черты заключаются в том, чтобы запустить ваш профилировщик и заставить его записывать:
__ restrict
, чтобы пообещать компилятору использовать псевдонимы. И еще кое-что, что мне нравится делать:
Поскольку многие проблемы производительности связаны с проблемами базы данных, я дам вам несколько конкретных вещей, на которые следует обратить внимание при настройке запросов и хранимых процедур.
Избегайте курсоров в большинстве баз данных. Также избегайте зацикливания. В большинстве случаев доступ к данным должен быть основан на наборах, а не на записи путем обработки записей. Это включает в себя отказ от повторного использования одной хранимой процедуры, если вы хотите вставить 1 000 000 записей за один раз.
Никогда не используйте select *, возвращайте только те поля, которые вам действительно нужны. Это особенно верно, если есть какие-либо соединения, так как поля соединения будут повторяться и, таким образом, вызывать ненужную нагрузку как на сервер, так и на сеть.
Избегайте использования коррелированных подзапросов. Используйте объединения (включая соединения с производными таблицами, где это возможно) (я знаю, что это верно для Microsoft SQL Server, но проверьте совет при использовании другой серверной части).
Index, index, index. И обновите эту статистику, если применимо к вашей базе данных.
Сделайте запрос sargable . Это означает избегать вещей, которые делают невозможным использование индексов, таких как использование подстановочного знака в первом символе предложения like или функции в соединении или в качестве левой части оператора where.
Используйте правильные типы данных. Быстрее выполнять математические вычисления даты в поле даты, чем пытаться преобразовать строковый тип данных в тип данных даты, а затем производить вычисления.
Никогда не помещайте какой-либо цикл в триггер!
Большинство баз данных есть способ проверить, как будет выполняться запрос. В Microsoft SQL Server это называется планом выполнения. Сначала проверьте их, чтобы увидеть, где находятся проблемные места.
При определении того, что нужно оптимизировать, учитывайте, как часто выполняется запрос, а также сколько времени требуется на выполнение. Иногда можно добиться большей производительности от небольшой настройки запроса, который выполняется миллионы раз в день, чем от удаления времени запроса long_running, который выполняется только один раз в месяц.
Используйте какой-нибудь инструмент профилировщика, чтобы узнать, что действительно отправляется в базу данных и из нее. Я могу вспомнить один раз в прошлом, когда мы не могли понять, почему страница так медленно загружалась, когда хранимая процедура была быстрой и через профилирование выяснилось, что веб-страница запрашивала запрос много раз, а не один раз.
Профилировщик также поможет вам определить, кто кого блокирует. Некоторые запросы, которые выполняются быстро в одиночку, могут стать очень медленными из-за блокировок от других запросов.
Иногда можно добиться большей производительности от небольшой настройки запроса, который выполняется миллионы раз в день, чем от удаления времени запроса long_running, который выполняется только один раз в месяц.Используйте какой-нибудь инструмент профилировщика, чтобы узнать, что действительно отправляется в базу данных и из нее. Я могу вспомнить один раз в прошлом, когда мы не могли понять, почему страница так медленно загружалась, когда хранимая процедура была быстрой и через профилирование выяснилось, что веб-страница запрашивала запрос много раз, а не один раз.
Профилировщик также поможет вам определить, кто кого блокирует. Некоторые запросы, которые выполняются быстро в одиночку, могут стать очень медленными из-за блокировок от других запросов.
Иногда можно добиться большей производительности от небольшой настройки запроса, который выполняется миллионы раз в день, чем от удаления времени запроса long_running, который выполняется только один раз в месяц.Используйте какой-нибудь инструмент профилировщика, чтобы узнать, что действительно отправляется в базу данных и из нее. Я могу вспомнить один раз в прошлом, когда мы не могли понять, почему страница так медленно загружалась, когда хранимая процедура была быстрой и через профилирование выяснилось, что веб-страница запрашивала запрос много раз, а не один раз.
Профилировщик также поможет вам определить, кто кого блокирует. Некоторые запросы, которые выполняются быстро в одиночку, могут стать очень медленными из-за блокировок от других запросов.
Чтобы выяснить, почему страница так медленно загружалась, когда хранимая процедура была быстрой и через профилирование выяснилось, что веб-страница запрашивала запрос много-много раз, а не один раз.Профилировщик также поможет вам определить, кто кого блокирует. Некоторые запросы, которые выполняются быстро в одиночку, могут стать очень медленными из-за блокировок от других запросов.
Чтобы выяснить, почему страница так медленно загружалась, когда хранимая процедура была быстрой и через профилирование выяснилось, что веб-страница запрашивала запрос много-много раз, а не один раз.Профилировщик также поможет вам определить, кто кого блокирует. Некоторые запросы, которые выполняются быстро в одиночку, могут стать очень медленными из-за блокировок от других запросов.
Дополнительные предложения:
Избегайте ввода-вывода : Любой ввод-вывод (диск, сеть, порты и т. Д.) всегда будет намного медленнее, чем любой другой выполнение вычислений, поэтому избавьтесь от любых операций ввода-вывода, которые вы делаете не обязательно.
Переместить ввод-вывод вперед : загрузить все данные, которые вы собираетесь для предварительного расчета, чтобы вы не повторялись ожидания ввода-вывода в ядре критического алгоритма (и, возможно, в результате повторных поисков диска, когда загрузка всех данных в одно обращение может избежать поиска).
Delay I / O : не записывайте свои результаты, пока расчет окончен, сохраните их в структуре данных и затем выбросьте это за один присест в конце, когда тяжелая работа готово.
Потоковый ввод-вывод : Для тех, кто достаточно смел, объедините "ввод-вывод" up-front 'или' Delay I / O 'с фактическим вычислением перевод загрузки в параллельный поток, так что пока вы загружаете больше данных, над которыми можете работать данные, которые у вас уже есть, или пока вы рассчитываете следующий пакет данных вы можете одновременно выписывать результаты из последней партии.
Единственным наиболее важным ограничивающим фактором сегодня является ограниченная пропускная способность памяти . Многоядерные процессоры только усугубляют ситуацию, поскольку пропускная способность распределяется между ядрами. Кроме того, ограниченная площадь микросхемы, предназначенная для реализации кешей, также разделена между ядрами и потоками, что еще больше усугубляет эту проблему. Наконец, межчиповая сигнализация, необходимая для поддержания когерентности различных кешей, также увеличивается с увеличением количества ядер. Это также добавляет штраф.
Это эффекты, которыми вам нужно управлять. Иногда с помощью микроуправления вашим кодом, но иногда с помощью внимательного рассмотрения и рефакторинга.
Во многих комментариях уже упоминается код, дружественный к кешу. У этого есть как минимум два различных варианта:
Первая проблема, в частности, связана с тем, чтобы сделать ваши шаблоны доступа к данным более регулярными, что позволяет аппаратному устройству предварительной выборки работать эффективно. Избегайте динамического выделения памяти, при котором объекты данных распределяются по памяти. Используйте линейные контейнеры вместо связанных списков, хэшей и деревьев.
Вторая проблема связана с улучшением повторного использования данных. Измените свои алгоритмы, чтобы они работали с подмножествами ваших данных, которые умещаются в доступном кэше, и повторно используйте эти данные в максимально возможной степени, пока они все еще находятся в кеше.
Более жесткая упаковка данных и обеспечение использования всех данных в строках кэша в циклы быстрого доступа, помогут избежать этих других эффектов и позволят разместить больше полезных данных в кэше.
позволяя аппаратному устройству предварительной загрузки работать эффективно. Избегайте динамического выделения памяти, при котором объекты данных распределяются по памяти. Используйте линейные контейнеры вместо связанных списков, хэшей и деревьев.Вторая проблема связана с улучшением повторного использования данных. Измените свои алгоритмы, чтобы они работали с подмножествами ваших данных, которые умещаются в доступном кэше, и повторно используйте эти данные в максимально возможной степени, пока они все еще находятся в кеше.
Более жесткая упаковка данных и обеспечение использования всех данных в строках кэша в циклы быстрого доступа, помогут избежать этих других эффектов и позволят разместить больше полезных данных в кэше.
позволяя аппаратному устройству предварительной загрузки работать эффективно. Избегайте динамического выделения памяти, при котором объекты данных распределяются по памяти. Используйте линейные контейнеры вместо связанных списков, хэшей и деревьев.Вторая проблема связана с улучшением повторного использования данных. Измените свои алгоритмы, чтобы они работали с подмножествами ваших данных, которые умещаются в доступном кэше, и повторно используйте эти данные в максимально возможной степени, пока они все еще находятся в кеше.
Более жесткая упаковка данных и обеспечение использования всех данных в строках кэша в циклы быстрого доступа, помогут избежать этих других эффектов и позволят разместить больше полезных данных в кэше.
Последние несколько% очень сильно зависят от процессора и приложений ....
Список можно продолжить ... Но такие вещи действительно последнее средство ...
Выполните сборку для x86 и запустите Valgrind / Cachegrind против кода. для правильного профилирования производительности. Или Texas Instruments ' CCStudio имеет прекрасный профилировщик. Тогда вы действительно узнаете, где сфокусироваться ...
Настройка ОС и фреймворка.
Это может показаться излишним, но подумайте об этом так: операционные системы и фреймворки предназначены для многих вещей . Ваше приложение делает только очень специфические вещи. Если бы вы могли заставить ОС выполнять именно то, что нужно вашему приложению, и ваше приложение понимало, как работает фреймворк (php, .net, java), вы могли бы намного лучше использовать свое оборудование.
Facebook, например, изменил некоторые элементы уровня ядра в Linux, изменил принцип работы memcached (например, они написали прокси-сервер memcached, а использовали udp вместо tcp ).
Другой пример - Window2008. У Win2K8 есть версия, в которой вы можете установить только базовую ОС, необходимую для запуска X-приложений (например, веб-приложений, серверных приложений). Это снижает значительную часть накладных расходов, которые ОС несет на запущенных процессах, и дает вам лучшую производительность.
Конечно, вы всегда должны добавлять больше оборудования в качестве первого шага ...
Кэширование! Дешевый способ (с точки зрения программиста) сделать что-либо быстрее - это добавить уровень абстракции кэширования в любую область перемещения данных вашей программы. Будь то ввод / вывод или просто передача / создание объектов или структур. Часто легко добавить кеши к заводским классам и читателям / писателям.
Иногда кеширование мало что дает, но это простой метод - просто добавить кеширование повсюду, а затем отключить его там, где это не помогает. Я часто обнаруживал, что это позволяет добиться огромной производительности без необходимости микроанализа кода.
The google way is one option "Cache it.. Whenever possible don't touch the disk"
Разделяй и властвуй
Если обрабатываемый набор данных слишком велик, перебирайте его фрагменты. Если вы сделали свой код правильно, реализация должна быть простой. Если у вас монолитная программа, теперь вы знаете лучше.
Такой общий оператор невозможен, это зависит от проблемной области. Некоторые возможности:
Поскольку вы прямо не указываете, что ваше приложение выполняет 100% вычислений:
Если вы использовали базу данных, и это был Microsoft SQL Server:
ЕСЛИ ваше приложение является чисто вычислительным, вы можете посмотреть на этот мой вопрос об оптимизации кеша для поворота больших изображений. Увеличение скорости ошеломило меня.
Это долгий путь, но, возможно, он дает представление, особенно если ваша проблема связана с визуализацией: Rotating-bitmap-in-code
Другой способ - максимально избегать динамического выделения памяти. Выделите несколько структур одновременно, освободите их сразу.
В противном случае определите свои самые жесткие циклы и опубликуйте их здесь, в псевдо или нет, с некоторыми структурами данных.
На языке с шаблонами (C ++ / D) вы можете попробовать распространять значения констант через аргументы шаблона. Вы можете сделать это даже для небольших наборов не совсем постоянных значений с помощью переключателя.
Foo(i, j); // i always in 0-4.
становится
switch(i)
{
case 0: Foo<0>(j); break;
case 1: Foo<1>(j); break;
case 2: Foo<2>(j); break;
case 3: Foo<3>(j); break;
case 4: Foo<4>(j); break;
}
Обратной стороной является давление кеша, поэтому это будет только выигрыш в глубоких или длительных деревьях вызовов, где значение является постоянным для продолжительность.