> instruction scheduling актуален всегда. Под x86-OoO процессоры есть много нюансов расположения команд. В т.ч. переупорядочивания обращений памяти несмотря на наличие в интеловских процах спекулятивного load-а.
Шедулинг с точки зрения зависимости по данным никого не интересует в условиях:
1) нехватки регистров
2) двухадресных инструкций
3) наличия сложных режимов адресации
4) наличия инструкций, использующих фиксированные регистры (сдвиги, cmpxchg, mul, etc)
Любые перестановки инструкций будут делаться исходя из соображений минимизации количества используемых регистров. И в случае x86 это зависит не только от области жизни переменных, потому что в зависимости от порядка инструкций могут появляться возможности для:
1) использования более «удобного» режима адресации
2) уменьшения количества пересылок (или даже числа используемых регистров, а значит и возможных чтений/записей в память), которые требуются из-за двухадресности инструкций
3) уменьшения количества пересылок и дополнительных регистров (и чтений/записей в память) из-за инструкций, которым необходим один и тот же фиксированный регистр.
Кому вообще нужна мифическая возможность поэкспериментировать с влиянием переупорядочивания обращений к памяти на OoO процессор, если это переупорядочивание может легко привести к добавлению дополнительных операций с памятью? Можно конечно делать переупорядочивание уже после того, как код сгенерирован, только вряд ли там что-либо получится сильно попереставлять.
> Если бы он ещё и работал, было бы совсем хорошо.
Так он не нужен похоже никому, потому никто об этом и не заботится.
> Логика из серии «мне не надо — значит и другим не надо» провальна изначально.
А я такой логикой не пользуюсь. Я просто смотрю на существующие компиляторы и вижу, что бэкенды и оптимизации для архитектур, отличных от x86, плохо поддерживаются, мало разрабатываются, и генерируют поганый код. Тут может быть два варианта: вселенский заговор, или это не интересует почти никого.
> IBM то чай с пауеров и SystemZ наваривает некисло
SGI на своих MIPSах и SGI OS тоже наваривали…
> MIPS — 90% роутеров малого и среднего звена. Хотите сказать, там производительность непринципиальна?
Непринципиальна. Много вы роутеров знаете на mips64? Попробуйте найти компилятор или собрать кросс-компилятор gcc для mips64el. Будете «приятно» удивлены.
> ppc, sparc… ну, наверно я в пьяном бреду ставил и ставлю…
это всё пережитки прошлого, оставшиеся по инерции у корпоративных монстров
Посмотреть бы ещё код без ассемблерных вставок, а то я например попытался восстановить цикл в Sub, и gcc-4.5 сгенерил такой же код, как в ассемблерной вставке.
> А потом, что? Вдруг пропала?
шедулинг живёт почти в каждом более-менее старом бэкенде, но обычно никак не тестируется и не дорабатывается, и не факт, что вообще работает, ибо для x86/x64 не актуален, а под другие мало кто тестирует.
> FYI Ни один консольный компилятор не умеет такого (VC/GCC/etc)
Вам именно modulo scheduling? «gcc -fmodulo-sched». Разработан, кстати, похоже IBMом в двухтысячных, опять же, потому что никому нафиг не сдался.
> сервера на Sparc
мертво
> Power6
почти мертво
> Консоли, 95% мобильных устройств
На самом деле единственный, кто тут может представлять интерес, это ARM. IBM с ppc в последние годы пытается вырваться за счёт консолей, но это мало что значит. Раньше playstation была на MIPS, но это не помешало MIPS умереть, вместе с SGI и SiCortex.
Всякие mips и ppc уходят в прошлое, суперкомпьютеры строятся на интелах и вообще видеокартах, с точки зрения высокопроизводительных вычислений мало что представляет интерес, кроме x64.
этот ваш шедулинг по зависимым данным — это почти тривиальная и хорошо изученная вещь, которая была реализована во многих компиляторах. В современных компиляторах ей почти не уделяется внимание, потому что на x64 шедулинг нужен только для помощи распределителю регистров, а эти самые
Неправильно отображается информация в разделе проверки выписанных штрафов ГИБДД.
1) Захожу в раздел, ввожу номер авто, вижу два выписанных штрафа.
2) Захожу опять, ввожу номер водительского удостоверения. При это должны показаться все штрафы, выписанные на все авто, зарегистрированные на владельца удостоверения. Но система выдаёт «Сведения об административных правонарушениях по указанным гос.номеру или водительскому удостоверению не найдены».
> Там всё начинается с того, что нужно в банкомате взять одноразовый пароль!
Такое не только в сбере, но и во многих приличных банках. Если что, то это нововведение визы: en.wikipedia.org/wiki/3-D_Secure
Одноразовые пароли — это только один из вариантов.
Да не смешите мои тапки. Первые две оптимизации относятся к реальным оптимизациям, и может с большой натяжкой их даже можно считать действительно сложными, но то, что делает компилятор C# — это тривиальные вещи.
Последние две «оптимизации» — это вообще что-то левое из разряда «помоги джиту работать быстрее», не относящееся к непосредственным оптимизациям.
Я не понимаю о чём речь. На вопрос «В Вашем варианте линкер, получается, работает с внутренним представлением компилятора в некотором intermediate-формате и сам генерирует код?» ответ положительный. Да, «компилятор» генерирует промежуточное представление в «объектных файлов» и потом они передаются «линкеру», который собирает объекты вместе и генерирует код.
Кто за что отвечает и что через что там внутри вызывается — это уже что-то не имеющее отношения к вопросу. Всё, что выполняет оптимизации и генерирует код можно назвать компилятором.
Да запарили вы уже с этим ABCE в jdk, который умеет тривиальные случаи удалять и будет только в седьмом jdk. Excelsior JET, прости хоспидя, и то лучше делает. =)
> большинство действительно сложных оптимизаций (которые требуют много времени) делаются при компиляции иисходники -> байт-код
Это враньё, по крайней мере для Java и C#. Если не согласны, то назовите хотя бы некоторые из этих «действительно сложных оптимизаций».
> Бесполезно сравнивать с С++, поскольку у последнего в разы
> больше возможностей за счёт библиотек. Как вы интегрируете тот же
> Perl модуль в программу на Delphi?
Вы так говорите, как будто это дерьмо, perl, можно нормально прикрутить к программе на C/C++. В теории то наверняка всё гладко, да только почти полное отсутствие документации (акромя каких-то обрубков а-ля embedded perl) и дерьмовость самого интерпретатора (чего только стоит вызов exit(code) при невозможности загрузить модуль) делают это практически невозможным. Использование макросов в API shared либы — это вообще гениальное решение, за которое нужно руки вырывать. Короче, проще делать обёртку для нужного модуля и запускать его во внешнем интерпретаторе, чем мучаться со всем этим дерьмом.
> Исторически одной из первых ОС с поддержкой асинхронного ввода-вывода стала Windows 2000.
Насколько я помню, асинхронный ввод-вывод и OVERLAPPED появились ещё в Windows NT 4.0, но этот динозавр уже не упоминается в текущей версии MSDN.
Ну и немного поправок/уточнений по поводу асинхронного i/o в windows.
1) callback-функции, передаваемые в функции чтения/записи — это отдельный механизм, называемый APC (Asynchronous Procedure Calls). Его можно использовать независимо от i/o и callback-функции работают через него. APC работает в пределах одного потока: асинхронный вызов можно назначить только в контексте одного конкретного потока. В случае i/o вызов будет сделан в контексте потока, инициализировавшего i/o.
2) IOCP != OVERLAPPED. IOCP — это интегрированный с планировщиком потоков механизм, который позволяет организовать эффективный пул потоков для работы с OVERLAPPED i/o на многопроцессорных машинах. По сути это thread safe очередь, в которую можно помещать элементы, даже не обязательно связанные с i/o (PostQueuedCompletionStatus). Туда же помещаются результаты операций OVERLAPPED i/o. Несколько потоков в цикле получают элементы из очереди и обрабатывают эти элементы. При этом:
a) Если несколько потоков ждут очередного элемента, то приоритет имеет поток, который последним запросил элемент. Это позволяет не тратить время на усыпление/пробуждение потока в случае, когда элемент очереди появился в момент запроса работающим потоком.
b) Потоки, хоть раз запросившие элементы из очереди, считаются ассоциированными с этой очередью. Планировщик потоков работает с ними специальным образом. Если на машине N процессоров, а в пуле M потоков, и M > N (microsoft рекомендует делать M = N * 2), то не более N потоков будут пробуждены независимо от того, сколько элементов оказалось в очереди — это позволяет минимизировать количество переключений контекста потоков на одном процессоре. Если по каким-то причинам один из этих N потоков уснул не при запросе очередного элемента (например на событии), то планировщик пробуждает один из оставшихся потоков, ждущих очередного элемента очереди.
Всё это позволяет добиться практически максимальной производительности на данном железе.
Вообще, мужики из microsoft двигаются в правильном направлении. в win2k, например, не было функции ConnectEx, позволяющей асинхронно установить исходящее соединение, приходилось создавать отдельный поток. В xp/2003 эта функция появилась, плюс появилась DisconnectEx, позволяющая переиспользовать сокет для нескольких подключений и экономить на вызове socket. Была ещё одна проблема — получение адреса по имени хоста было только синхронным (getaddrinfo), в vista/2008 добавили асинхронную версию (GetAddrInfoEx). API постепенно улучшается и это радует.
P. S. А вообще, boost.asio решает проблемы с эффективными кросплатформенными сетевыми приложениями ;)
У вас там на ARMе что-ли количество регистров бесконечное? )))
А пацаны то уже несколько десятков лет пытаются регистры распределить хорошо, и не знают, что табличным способом всё делается.
Вы бы хоть прежде чем писать такое хотя бы уж статью в википедии про register allocation почитали…
Единственный известный мне компилятор (не только статический, а вообще, включая jit), который худо-бедно умеет векторизовать циклы на SSE — это intel C/C++/Fortran compiler.
Векторизация циклов — это такая нехилая по сложности и времени задача, для успешного выполнения которой в более-менее реальных случаях требуется alias analysis и глобальный анализ кода. Вы правда думаете, что jit может себе такое позволить? ;) Да там как бы удаление проверок выхода за границы массива (без чего векторизация не возможна) уже за счастье считается, а Вы тут на векторизацию замахнулись.
Например, jit в hotspot vm только недавно научился удалять проверки границ массивов в самых простых случаях.
Вот и получается, что на словах — «оптимизация под конкретный процессор», а на деле — хер с маслом, разве что скалярные вычисления на SSE делать, да какой-нибудь cmov заиспользовать.
Шедулинг с точки зрения зависимости по данным никого не интересует в условиях:
1) нехватки регистров
2) двухадресных инструкций
3) наличия сложных режимов адресации
4) наличия инструкций, использующих фиксированные регистры (сдвиги, cmpxchg, mul, etc)
Любые перестановки инструкций будут делаться исходя из соображений минимизации количества используемых регистров. И в случае x86 это зависит не только от области жизни переменных, потому что в зависимости от порядка инструкций могут появляться возможности для:
1) использования более «удобного» режима адресации
2) уменьшения количества пересылок (или даже числа используемых регистров, а значит и возможных чтений/записей в память), которые требуются из-за двухадресности инструкций
3) уменьшения количества пересылок и дополнительных регистров (и чтений/записей в память) из-за инструкций, которым необходим один и тот же фиксированный регистр.
Кому вообще нужна мифическая возможность поэкспериментировать с влиянием переупорядочивания обращений к памяти на OoO процессор, если это переупорядочивание может легко привести к добавлению дополнительных операций с памятью? Можно конечно делать переупорядочивание уже после того, как код сгенерирован, только вряд ли там что-либо получится сильно попереставлять.
> Если бы он ещё и работал, было бы совсем хорошо.
Так он не нужен похоже никому, потому никто об этом и не заботится.
> Логика из серии «мне не надо — значит и другим не надо» провальна изначально.
А я такой логикой не пользуюсь. Я просто смотрю на существующие компиляторы и вижу, что бэкенды и оптимизации для архитектур, отличных от x86, плохо поддерживаются, мало разрабатываются, и генерируют поганый код. Тут может быть два варианта: вселенский заговор, или это не интересует почти никого.
> IBM то чай с пауеров и SystemZ наваривает некисло
SGI на своих MIPSах и SGI OS тоже наваривали…
Непринципиальна. Много вы роутеров знаете на mips64? Попробуйте найти компилятор или собрать кросс-компилятор gcc для mips64el. Будете «приятно» удивлены.
> ppc, sparc… ну, наверно я в пьяном бреду ставил и ставлю…
это всё пережитки прошлого, оставшиеся по инерции у корпоративных монстров
шедулинг живёт почти в каждом более-менее старом бэкенде, но обычно никак не тестируется и не дорабатывается, и не факт, что вообще работает, ибо для x86/x64 не актуален, а под другие мало кто тестирует.
> FYI Ни один консольный компилятор не умеет такого (VC/GCC/etc)
Вам именно modulo scheduling? «gcc -fmodulo-sched». Разработан, кстати, похоже IBMом в двухтысячных, опять же, потому что никому нафиг не сдался.
> сервера на Sparc
мертво
> Power6
почти мертво
> Консоли, 95% мобильных устройств
На самом деле единственный, кто тут может представлять интерес, это ARM. IBM с ppc в последние годы пытается вырваться за счёт консолей, но это мало что значит. Раньше playstation была на MIPS, но это не помешало MIPS умереть, вместе с SGI и SiCortex.
Всякие mips и ppc уходят в прошлое, суперкомпьютеры строятся на интелах и вообще видеокартах, с точки зрения высокопроизводительных вычислений мало что представляет интерес, кроме x64.
> in-order процессоры
фактически мертвы.
1) Захожу в раздел, ввожу номер авто, вижу два выписанных штрафа.
2) Захожу опять, ввожу номер водительского удостоверения. При это должны показаться все штрафы, выписанные на все авто, зарегистрированные на владельца удостоверения. Но система выдаёт «Сведения об административных правонарушениях по указанным гос.номеру или водительскому удостоверению не найдены».
Такое не только в сбере, но и во многих приличных банках. Если что, то это нововведение визы: en.wikipedia.org/wiki/3-D_Secure
Одноразовые пароли — это только один из вариантов.
Последние две «оптимизации» — это вообще что-то левое из разряда «помоги джиту работать быстрее», не относящееся к непосредственным оптимизациям.
Кто за что отвечает и что через что там внутри вызывается — это уже что-то не имеющее отношения к вопросу. Всё, что выполняет оптимизации и генерирует код можно назвать компилятором.
Странное тоже встречается в нашем мире ))
gcc.gnu.org/onlinedocs/gccint/LTO.html
msdn.microsoft.com/en-us/library/xbf3tbeh.aspx
open64: опция -ipa
Это враньё, по крайней мере для Java и C#. Если не согласны, то назовите хотя бы некоторые из этих «действительно сложных оптимизаций».
> больше возможностей за счёт библиотек. Как вы интегрируете тот же
> Perl модуль в программу на Delphi?
Вы так говорите, как будто это дерьмо, perl, можно нормально прикрутить к программе на C/C++. В теории то наверняка всё гладко, да только почти полное отсутствие документации (акромя каких-то обрубков а-ля embedded perl) и дерьмовость самого интерпретатора (чего только стоит вызов exit(code) при невозможности загрузить модуль) делают это практически невозможным. Использование макросов в API shared либы — это вообще гениальное решение, за которое нужно руки вырывать. Короче, проще делать обёртку для нужного модуля и запускать его во внешнем интерпретаторе, чем мучаться со всем этим дерьмом.
Насколько я помню, асинхронный ввод-вывод и OVERLAPPED появились ещё в Windows NT 4.0, но этот динозавр уже не упоминается в текущей версии MSDN.
Ну и немного поправок/уточнений по поводу асинхронного i/o в windows.
1) callback-функции, передаваемые в функции чтения/записи — это отдельный механизм, называемый APC (Asynchronous Procedure Calls). Его можно использовать независимо от i/o и callback-функции работают через него. APC работает в пределах одного потока: асинхронный вызов можно назначить только в контексте одного конкретного потока. В случае i/o вызов будет сделан в контексте потока, инициализировавшего i/o.
2) IOCP != OVERLAPPED. IOCP — это интегрированный с планировщиком потоков механизм, который позволяет организовать эффективный пул потоков для работы с OVERLAPPED i/o на многопроцессорных машинах. По сути это thread safe очередь, в которую можно помещать элементы, даже не обязательно связанные с i/o (PostQueuedCompletionStatus). Туда же помещаются результаты операций OVERLAPPED i/o. Несколько потоков в цикле получают элементы из очереди и обрабатывают эти элементы. При этом:
a) Если несколько потоков ждут очередного элемента, то приоритет имеет поток, который последним запросил элемент. Это позволяет не тратить время на усыпление/пробуждение потока в случае, когда элемент очереди появился в момент запроса работающим потоком.
b) Потоки, хоть раз запросившие элементы из очереди, считаются ассоциированными с этой очередью. Планировщик потоков работает с ними специальным образом. Если на машине N процессоров, а в пуле M потоков, и M > N (microsoft рекомендует делать M = N * 2), то не более N потоков будут пробуждены независимо от того, сколько элементов оказалось в очереди — это позволяет минимизировать количество переключений контекста потоков на одном процессоре. Если по каким-то причинам один из этих N потоков уснул не при запросе очередного элемента (например на событии), то планировщик пробуждает один из оставшихся потоков, ждущих очередного элемента очереди.
Всё это позволяет добиться практически максимальной производительности на данном железе.
Вообще, мужики из microsoft двигаются в правильном направлении. в win2k, например, не было функции ConnectEx, позволяющей асинхронно установить исходящее соединение, приходилось создавать отдельный поток. В xp/2003 эта функция появилась, плюс появилась DisconnectEx, позволяющая переиспользовать сокет для нескольких подключений и экономить на вызове socket. Была ещё одна проблема — получение адреса по имени хоста было только синхронным (getaddrinfo), в vista/2008 добавили асинхронную версию (GetAddrInfoEx). API постепенно улучшается и это радует.
P. S. А вообще, boost.asio решает проблемы с эффективными кросплатформенными сетевыми приложениями ;)
У вас там на ARMе что-ли количество регистров бесконечное? )))
А пацаны то уже несколько десятков лет пытаются регистры распределить хорошо, и не знают, что табличным способом всё делается.
Вы бы хоть прежде чем писать такое хотя бы уж статью в википедии про register allocation почитали…
Векторизация циклов — это такая нехилая по сложности и времени задача, для успешного выполнения которой в более-менее реальных случаях требуется alias analysis и глобальный анализ кода. Вы правда думаете, что jit может себе такое позволить? ;) Да там как бы удаление проверок выхода за границы массива (без чего векторизация не возможна) уже за счастье считается, а Вы тут на векторизацию замахнулись.
Например, jit в hotspot vm только недавно научился удалять проверки границ массивов в самых простых случаях.
Вот и получается, что на словах — «оптимизация под конкретный процессор», а на деле — хер с маслом, разве что скалярные вычисления на SSE делать, да какой-нибудь cmov заиспользовать.