Главное меню
Мы солидарны с Украиной. Узнайте здесь, как можно поддержать Украину.

В погоне за скоростью

Автор Алексей Гринь, июля 25, 2009, 22:55

0 Пользователи и 1 гость просматривают эту тему.

Алексей Гринь

Цитата: wienski от июля 25, 2009, 19:49
Пропагандируйте ассемблер для архитектуры x86-64
Вот кстати, реализована ли такая вещь, немного не в тему, но мучает уж больно:
  1) есть некий джиттер, который некий байткод на лету генерит в нативный код (или он с самого начала нативный, но секции особо помечены)
  2) есть ооочень большой (не в смысле длины кода; в смысле продолжительности) и ресурсоёмкий цикл (например, отрисовка 3d-визуализация)
  3) в обычном случае мы можем иметь кучи проверок типа if(b) { ... }, где b — нечто булево
  4) в нашем предполагаемом случае компилятор, зная, что в данном случае b всегда (или очень большое количество раз) будет false, просто вырезает весь ненужный код на лету (хотя можно и реализовать пре-). Т.е. вырезается сравнение на буль, вырезается весь код внутри (никаких джампов...)
  5) т.е. в зависимости от условий будет вызываться та вариация функции, которая содержит проверку и содержит код или та, в которой это не нужно...

Это что-то из области полиморфного кода... Есть нечто подобное уже реализованное? Или самому экспериментировать? :) Просто, читая разные энджинс и сам пиша, я вижу, сколько ненужных проверок проводится за одну итерацию цикла... Если была бы возможность ненужный код попросту вырезать, создавая копию метода, и заменяя адрес метода на новую копию, много бы тактов было схоронено...

Ещё раз, если непонятно, есть такой случай:

Цитироватьif(x)
  drawSomething1();
drawSomething2();

Далее, когда это нужно, мы делаем мегасуперполиморфной системе некий сигнал, что, дескать, x пока что долгое время будет фолс... Тогда компилятор (скорее среда исполнения) для такого случая делает копию предыдущего метода, но вырезая сравнения, что получается нечто такое:

ЦитироватьdrawSomething2();

И вместо предыдущей функции теперь будет вызываться эта. Для ресурсоёмких программ и низкоресурсных систем это очень кузяво! Лишине опкоды попросту не выполняются, ибо их нет!
Потом программа может дать сигнал, дескать, x теперь true. И тогда адрес функции заменяется на предыдущий, полный...
Мне бы вот такую среду поиметь ) Ессно, что и в каком-нить Си(++) это всё можно реализовать, но через ж. и с большим количеством копипастов...
肏! Τίς πέπορδε;

wienski

Своеобразный "модуль предсказания переходов" нового столетия? )
Я вот не читал о таком. Впрочем, ни дотнетом, ни джавой не балуюсь. Я бы просто код писал таким образом, чтобы флаг проверять реже  :donno:
Вообще, это должно сложно реализовываться, т.к. придется бежать по коду вперед, ветвясь на всех условных переходах, и отлавливать изменения флага, а это небыстро будет, имхо.
Думается, или писать сразу как-то иначе, или производительность будет увеличиваться только в самых запущенных случаях...

Алексей Гринь

Это также может быть рантаймная замена некоторых уже вычисленных значений на константы...
Например, есть нечто типа
Цитироватьx = y / 10 + 70;
Значения x и y не известны на момент компиляции.

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

Дух Кармака, спаси и сохрани.
肏! Τίς πέπορδε;

Алексей Гринь

Цитата: wienski от июля 25, 2009, 23:05
Вообще, это должно сложно реализовываться, т.к. придется бежать по коду вперед, ветвясь на всех условных переходах, и отлавливать изменения флага, а это небыстро будет, имхо.
Это очень хорошо подходит для систем визуализации. Состояния объектов меняются НАМНОГО реже, чем отрисовываются кадры. То бишь, скажем, у объекта состояние меняется 5 раз в минуту, а отрисовывается он 60 * 60 раз в минуту. Тем более что уже откомпиленный код кешируется, не подразумевается, что каждый раз будет рекомпиляция. Просто подставляется иная уже откомпиленная однажды функция. Это ОДНА запись в память при изменении состояния (учитывая, что вариация метода уже прекомпилена), вместо СОТЕН проверок (и каждая - чтение из рандом-памяти) и вычислений КАЖДЫЙ кадр.
Есть наверное ещё где можно применить.
Плюс никто не мешает делает перекомпиляцию в параллельном потоке.

Тем более что булевым может быть не просто какое-то значение из памяти, а результат некоего вычисления... Ускорение тем более на лицо :)
肏! Τίς πέπορδε;

Чайник777

DAZU brauchte Hitler 12 Jahre Zeit.

Алексей Гринь

肏! Τίς πέπορδε;

wienski

Т.е., условно говоря, ставить перед такой проверкой jmp и забивать его nop'ами (разумеется, в опкодах рантайма), если цикл может выполниться?

Алексей Гринь

Цитата: wienski от июля 25, 2009, 23:05
Я вот не читал о таком. Впрочем, ни дотнетом, ни джавой не балуюсь
То не обязательно байткод, это я как пример. Может быть и 100% натив + пара сотен килобайт рантайма под "кодоманипулятор" :)
肏! Τίς πέπορδε;

Алексей Гринь

Цитата: wienski от июля 25, 2009, 23:21
Т.е., условно говоря, ставить перед такой проверкой jmp и забивать его nop'ами (разумеется, в опкодах рантайма), если цикл может выполниться?
Это light-версия :) В dark-версии идёт уплотнение :) Это не так ресурсоёмко, как кажется :) Ещё раз повторюсь: 1) не предполагается, что каждый раз будет рекомпиляция. Один закешированный раз на один случай. В некоторых случаях можно обойтись прекомпиляцией всех вариантов заранее (но скажется на размере образа программы...) 2) не обязательно это делать для всего и вся, это действительно только угробит перфоманс. Только для избранных участков. В хелловорлдах необязательно :)
肏! Τίς πέπορδε;

Чайник777

Алексей Гринь, то о чём вы пишете - это всё очень эррор-проне.
DAZU brauchte Hitler 12 Jahre Zeit.

wienski

А мне кажется, что в память пишется все же не очень быстро...
Одно дело забить пару байт нопами, другое — перезаписать участок кода в память с уплотнением.
Почему-то мне кажется, что первое менее ресурсозатратно с учетом сегодняшних объемов памяти.
:UU:

Алексей Гринь

Цитата: Чайник777 от июля 25, 2009, 23:26
Алексей Гринь, то о чём вы пишете - это всё очень эррор-проне.
Эррор-проне для разработчиков такой системы, но не для энд-юзеров. А то по вашему все системы на основе джиттеров - тоже эррор-проне? Явоваская девиртуализация методов в рантайме лет 20 назад тоже казалась чудом, и чо? Оно тоже эррор-проне? Ынтерпрайз-гламур этого не замечает :D
肏! Τίς πέπορδε;

Алексей Гринь

Цитата: wienski от июля 25, 2009, 23:27
Одно дело забить пару байт нопами, другое — перезаписать участок кода в память с уплотнением.
Почему-то мне кажется, что первое менее ресурсозатратно с учетом сегодняшних объемов памяти.
Ну вообще главное идея. Какая там реализация быстрее — это надо бенчмарки проводить ;) Иногда nop'ы в мультитредовых приложениях только ускоряют процесс :)
肏! Τίς πέπορδε;

wienski


Алексей Гринь

Вообщем, это всё круто, когда идёт много-много вычислений и много-много чтений, но мало реальных изменений состояний объектов (тут внезапно запахло функциональщиной :) ). Поэтому легче всё законстантить, чем каждый раз одноооо и тоооже...

Хаскель вот умница кэширует входные значения и результаты для функций, ежели функция больше по трудовым затратам (вычисление какого-нить множества мандельброта яипу), чем простой поиск по кэшу. Зачем каждый раз как идиот вычислять, если мы это уже вычисляли? Тут типа то же самое, но чрез манипуляции с кодом, а не с данными...
肏! Τίς πέπορδε;

Алексей Гринь

С другой стороны, это может быть банальный препроцессор для си, с заранее известным на этапе компиляции количеством типов сигналов и полей кода, обрамлённых для послушания таким сигналам. Т.е. все вариации функции создаются в компайл-тайм. Образ приложения разбухает, что плохо (если в функции есть хотя бы одно поле, то это уже две прекомпиленные вариации).
+ Нам нужна некая функция, например типа __signal(FUNC, VAR, BOOL);, которая сама уже в рантайме маппит нужные вариации функции к нужным ситуациям.
В этот способ эррор-проне в пятьсот раз меньше, легче реализуем и никакого практически рантайма кроме функции сигналирования.
Как-нить поэкспериментирую ;)

P.S. Хотя всё-таки нутром чую, что где-то кто-то это уже реализовал ;) Но не знаю как назвать, шоб загуглить.
肏! Τίς πέπορδε;

oort

На языках с хорошим препроцессором это пишется довольно легко. Для языков с плохим препроцессорм, типа сей, можно написать маленький препроцессор на каком-нибудь легком языке. Что-нибудь типа


# for (i = 1; i <= 2; i++) {
   void func$i (...) {
#   if (i == 1) {
      if (flag) {
        something;
      }
#   }
      something2;
   }
# }

if (verify) {
  func = func1;
} else {
  func = func2;
}
for (...) {
  for (...) {
    func (args);
  }
}

myst

Цитата: oort от июля 26, 2009, 12:27
Для языков с плохим препроцессорм, типа сей, можно написать маленький препроцессор на каком-нибудь легком языке.
Зачем изобретать велотренажёр? Готовые давно есть.

myst

Дочитал тему, и вспомнился мне проект Juice Михаэля Франца. По-моему, уже в той его статье было про автоматическую повторную трансляцию на основе данных профилирования.

myst

Ой! Статья оказалась его диссертацией. :)

Алексей Гринь

Цитата: myst от июля 26, 2009, 20:32
Зачем изобретать велотренажёр? Готовые давно есть.
Только о них в этой теме почему-то умалчивается, аха.

Цитата: oort от июля 26, 2009, 12:27
На языках с хорошим препроцессором это пишется довольно легко.
Например? Препроцессор он препроцессит. Он не может в рантайме решать что как делать.

Цитата: myst от июля 26, 2009, 20:34
Дочитал тему, и вспомнился мне проект Juice Михаэля Франца. По-моему, уже в той его статье было про автоматическую повторную трансляцию на основе данных профилирования.
Так пролистал, вроде он там только говорит о хранении байткода в виде окученных синтаксических деревьев, это я уже где-т видел.
肏! Τίς πέπορδε;

Алексей Гринь

Во-о-о-о.
Написал тест на сравнение обычного кода и "полиморфного" (не знаю, правильно ли употреблять здесь этот термин ;)).

Удивлён результатам и доволен собой.

Мне это ведь в какой области важно? В области визуализации. Там обычно как — один тред рисует объекты, другой (или несколько) меняет состояния объектов. При отрисовке одного объекта происходит куча проверок (особенно если используется некое обобщённое представление 3d-объектов) — имеет ли объект подЪузлы, кто рядом (для отражений) и вообще он чайник или бублик. Так вот, идея в том, что тред, меняющий состяния, каждый раз при изменении нужного поля сигналит об этом треду-рисующему, который маппит одну функцую в нужную другую (в которой все избыточные, не нужные конкретно сейчас проверки (да и вообще действия) вырезаны).

Так вот, тест выдал аткие результаты (скомпилено gcc -О2):
ЦитироватьPlain code: 10266 ms
Polymorph.: 6156 ms

Process returned 0 (0x0)   execution time : 16.453 s
Press any key to continue.

Для чистоты эксперимента поменял местами вызовы:
ЦитироватьPolymorph.: 6171 ms
Plain code: 10282 ms

Process returned 0 (0x0)   execution time : 16.468 s
Press any key to continue.

Результат, такскать, на лицо (на лице). Сей метод помогает сэкономить до 40% времени и сил процессора (если, конечно, правильно сигналить).

К посту приаттачен апофеоз моего быдлокодерского гения.
В тесте многотопоточное приложение для простоты эмулируется средствами однопоточного. Я мало думал над синхронизацией (thread-safety), однако. Как-нибудь потом.
肏! Τίς πέπορδε;

Алексей Гринь

Цитата: oort от июля 26, 2009, 12:27
На языках с хорошим препроцессором это пишется довольно легко. Для языков с плохим препроцессорм, типа сей, можно написать маленький препроцессор на каком-нибудь легком языке. Что-нибудь типа
Если вы имеете под "языками с хорошим препроцессором" какой-нибудь лисп и компания, то они все куда медленней и сей, если им пользоваться как обычно. Вся идея насморку. Хотим быстрее, но препроцессор слабый, нам советуют препроцессор языка Х, реализации для которого в большинстве случаев медленнее обычного си без выкрутасов в несколько раз... А мы-то хотим быстрее...
肏! Τίς πέπορδε;

oort

Цитата: Алексей Гринь от июля 27, 2009, 10:19
Цитата: oort от июля 26, 2009, 12:27
На языках с хорошим препроцессором это пишется довольно легко. Для языков с плохим препроцессорм, типа сей, можно написать маленький препроцессор на каком-нибудь легком языке. Что-нибудь типа
Если вы имеете под "языками с хорошим препроцессором" какой-нибудь лисп и компания, то они все куда медленней и сей, если им пользоваться как обычно. Вся идея насморку. Хотим быстрее, но препроцессор слабый, нам советуют препроцессор языка Х, реализации для которого в большинстве случаев медленнее обычного си без выкрутасов в несколько раз... А мы-то хотим быстрее...
Маленький препроцессор на перле, который добавит #for к сишному препроцессору, мне кажется, много времени не займет. А это все, что нужно. С помощью этого препроцессора пишется `проверочно-зависимый' код, в который проверки, благодаря препроцессору, вставляются без дублирования полезного кода. Да, реальную реал-таймовую проверку придется писать руками. Две переменные. Одна поднимается при любом изменении второй, вторая когда нужно. Если поднята первая переменная, переназначаем полиморфную функцию и опускаем первую переменную.

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

Алексей Гринь

Цитата: oort от июля 27, 2009, 10:59
Да, это, разумеется, не так удобно, как при реализации этой идеи в компиляторе.
Я вот как раз поднял тему с того, что искал, есть ли уже такие системы реализованные.

Тут же опущение кода по проверке на булевость — лишь одна десятая того, что ещё можно замутить на такой концепции :) Правда, размер бинарников будет экспоненциально-геометрически расти :'( (и тут хопа! - нам какбэ говорят джиттеры упакованных синтаксических деревьев :) )
肏! Τίς πέπορδε;

Быстрый ответ

Обратите внимание: данное сообщение не будет отображаться, пока модератор не одобрит его.

Имя:
Имейл:
Проверка:
Оставьте это поле пустым:
Наберите символы, которые изображены на картинке
Прослушать / Запросить другое изображение

Наберите символы, которые изображены на картинке:

√36:
ALT+S — отправить
ALT+P — предварительный просмотр