Javascript-форум (https://javascript.ru/forum/)
-   Оффтопик (https://javascript.ru/forum/offtopic/)
-   -   V8 нахождение деоптимизаций (https://javascript.ru/forum/offtopic/34526-v8-nakhozhdenie-deoptimizacijj.html)

melky 08.01.2013 22:21

V8 нахождение деоптимизаций
 
Чтобы понимать, о чём суть, можно ознакомиться с этой статьёй.

Презентация об оптимизации компилирования JavaScript в V8

Думаю, все знают о том, как управлять качеством компилируемого JavaScript - наверняка это относится не только к V8, но и к FireFox'овским компилятором, которые он меняет через неск. версий (сейчас я про JIT компиляторы).

Вкратце, всё сводится к тому, чтобы как можно более статично писать код - т.е. не использовать разные типы для одной переменной, не добавлять новые свойства к объекту и т.д. ... короче, чтобы получить действительно быстрый JavaScript, нужно как-то особенно писать.

Тут то и возникает проблема. Вот вроде бы ты и написал, как "надо". Как теперь проверить, деоптимизируется ли функция? Вариант с бенчмарками отпадает, как самый примитивный.


Я немного пощупал V8 (точнее, Chrome, ну да ладно) на предмет логирования его действий при обработке моего скрипта.

Всё свелось к простейшему созданию особого ярлыка для Chrome, и перетаскиванию HTML-файла со скриптом на этот ярлык (я пока на виндовсе :)) :
Код:

C:\Users\LOL\AppData\Local\Google\Chrome\Application\chrome.exe --no-sandbox
--js-flags="--logfile=%t.log --trace-opt --trace-deopt  --noprof-lazy --prof-auto --log-gc" 
--incognito

Все эти флаги были бережно нагуглены:
Profiling Chromium With V8
Profiling Chromium With V8 №2

после этого в папке с chrome.dll появился файл лога, содержащий какие-то записи сумашедшего летописца.

Вроде я указал, что мне нужно записывать , но поиск по файлу ничего путёвого, кроме какого-то левого профилирования, не выдал.

Лог сам весит 211 кб :) Первое упоминание о моём файле аж на 656'й строке.

Код:

code-creation,Function,0x369363a0,7992," file:///E:/Max/melanim/dist/mel-anim.compiled.js:5",0x26828704,~
code-creation,Script,0x369382e0,208,"file:///E:/Max/melanim/dist/mel-anim.compiled.js",0x268287a8,~

Наверное, это начало разбора главного большого замыкания... интересно, что означают эти записи ?

ещё немного бреда :
Код:

code-creation,LoadIC,0x3693d660,98,"prefix"
code-creation,LoadIC,0x3693d660,98,"prefix"
code-creation,LoadIC,0x3693d6e0,98,"lowPrefix"
code-creation,LoadIC,0x3693d6e0,98,"lowPrefix"
code-creation,KeyedLoadIC,0x3693d760,110,"animation"
code-creation,KeyedLoadIC,0x3693d760,110,"animation

Что такое LoadIC? А KeyedLoadIC?

особенно интересны эти моменты :
Код:

code-creation,LazyCompile,0x36955160,232,"type.array file:///E:/Max/melanim/dist/mel-anim.compiled.js:73",0x268269a4,~
code-creation,LazyCompile,0x36955260,544,"type.array file:///E:/Max/melanim/dist/mel-anim.compiled.js:73",0x268269a4,*

Что означают разные символы на концах строк (~ и *)? что такое LazyCompile?

Короче, куча вопросов, и ответов нет. Попытки гугления при водят только к непонятной документации.

Я хотел найти deopt'ы. Где они ?) Кто-нибудь вообще занимался этим?

Deff 08.01.2013 22:32

:( Пока нашел только это http://qps.ru/y5SKO

kobezzza 08.01.2013 22:47

Излишняя оптимизация зло, которое может превратить код в какашку:)

melky 08.01.2013 23:32

Цитата:

Сообщение от Deff (Сообщение 226115)
:( Пока нашел только это http://qps.ru/y5SKO

Я это видел. К сожалению, в этих логах я не нашёл ничего полезного :(

Цитата:

Сообщение от kobezzza (Сообщение 226116)
Излишняя оптимизация зло, которое может превратить код в какашку:)

Думаешь? хз хз ... в моём прокте важна каждая наносекунда - именно из-за неё может появиться тормоз и снизиться ФПС, ради чего всё затевалось.

Кстати, код для оптимизаций меняется не так сильно и совсем не уродуется :
Parser.prototype.parse = function (s) {
  var length = '', i = 0;
  while (i < s.length) {
    if (s[i] == '\u0000') {
      length = Number(length);
      this.emit('data', s.substr(i + 1, length));

-      s = s.substr(i + 1 + x);
-      i = 0; 
+      i += length + 1;

      length = '';
    } else {
      length += s[i++];
    }
  }
};

(я думаю, понятно, что за плюсы и минусы слева:) пример из презентации)

Самое главное - обьяснить компилятору, что тут именно тот привычный типизированный быстрый код какого-нибудь C++, написанный на JavaScript;

Всё, что я хочу - прочитать эти сказки о типизации, рассказанные компилятором в отношении моего кода.

kobezzza 08.01.2013 23:39

melky, если оптимизация реально нужна, то конечно нужно её делать, я лишь хотел сказать, что прочитав статью не нужно всем дружно бросаться переписывать наш код под каждый интерпретатор и фапать на мистический наносекунды:)

Цитата:

Кстати, код для оптимизаций меняется не так сильно и совсем не уродуется
В твоём случае да, но я имел ввиду скорее общий случай оптимизаций (будь то алгоритмическая и т.д.), к примеру я переписал для будущей версии своего Collection метод extend (интерфейс как в jQuery) с классической реализации рекурсией на рекурсию развёрнутую в массив: оптимизация в среднем в 2.5 раза, но смысл кода реально понять сложнее.

Collection.extend = function (deep, target, var_args) {
	deep = deep || false;
	target = target || {};
	var aLength = arguments.length,
		
		aEl, key,
		src, copy,
		copyIsArray,
		
		i = 1, j,
		
		stack = [],
		level,
		
		last, lastEl;
	
	// Внешний цикл по расширяющим аргументам
	while (++i < aLength) {
		if ((aEl = arguments[i])) {
			// Цикл для эмуляции рекурсии
			while (aEl) {
				level = [];
				
				// Цикл по свойствам объекта
				for (key in aEl) {
					src = target[key];
					copy = aEl[key];
					
					// Защита от бесконечного копирования
					if (target === copy) { continue; }
					
					// Рекурсивное копирование свойств
					// (рекурсия развёрнута)
					if (
						deep && copy
						&& ((copyIsArray = Array.isArray(copy)) || typeof copy === 'object')
					) {
						// Если копируемое свойство - массив
						if (copyIsArray) {
							copyIsArray = false;
							clone = src && Array.isArray(src) ? src : [];
						
						// Если копируемое свойство - объект
						} else {
							clone = src && typeof src === 'object' ? src : {};
						}
						
						// Запоминаем вложенность, чтобы в дальнейшем к ней вернуться
						level.push({
							target: target,
							clone: clone,
							
							aEl: aEl,
							copy: copy,
							
							key: key
						});
					} else {
						target[key] = copy;
					}
				}
				
				// Если на уровне имеются вложенности,
				// то добавляем новый уровень в стек и
				// проиводим сдвиг в крайне правый элемент нового уровня
				if ((last = level.length)) {
					stack.push(level);
					
					last = level[last - 1];
					// Ставим флаг, что элемент учавствует в обходе
					last['__COLLECTION_TMP__'] = true;
					
					// Устанавливаем новую точку отсчёта
					target = last.clone;
					aEl = last.copy;
					
				// На уровне нет вложенностей
				} else if ((last = stack.length)) {
					j = stack.length;
					while (j--) {
						lastEl = stack[j][stack[j].length - 1];
						
						// Если звено не имеет детей и уже было использовано,
						// то удаляем крайне правый элемент и сдвигаем позицию обхода
						if (lastEl['__COLLECTION_TMP__'] && !stack[j + 1]) {
							lastEl.target[lastEl.key] = target;
							target = lastEl.target;
							aEl = lastEl.aEl;
							stack[j].pop();
							
							// В звене не осталось элементов,
							// значит его можно удалить
							if (!stack[j].length) {
								stack.pop();
							}
						
						// Если первое условие не верно,
						// значит продолжать цикл нет смысла
						} else {
							break;
						}
					}
					
					// Устанавливаем новую позицию обхода
					if ((last = stack.length)) {
						last = stack[last - 1];
						lastEl = last[last.length - 1];
						lastEl['__COLLECTION_TMP__'] = true;
						
						target = lastEl.clone;
						aEl = lastEl.copy;
					}
				}
				
				// Операция закончена
				if (!stack.length && !level.length) {
					break;
				}
			}
		}
	}
	
	return target;
};

melky 09.01.2013 00:42

Цитата:

Сообщение от kobezzza
В твоём случае да, но я имел ввиду скорее общий случай оптимизаций (будь то алгоритмическая и т.д.), к примеру я переписал для будущей версии своего Collection метод extend (интерфейс как в jQuery) с классической реализации рекурсией на рекурсию развёрнутую в массив: оптимизация в среднем в 2.5 раза, но смысл кода реально понять сложнее.

Согласен, сначала нужно оптимизировать алгоритмически, а потом заниматься этими изысками. Так и сделаю, отложу муравьёв на потом. :)

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

kobezzza 09.01.2013 00:54

Цитата:

Сообщение от melky (Сообщение 226144)
Имхо следует в dev-версии оставлять самый понятный код - т.е. рекурсию.

Рекурсия в контексте моего Collection плодит много лишних тормозов, а так я тока за неё, т.к. очень простое и элегантное решение.

Цитата:

Сообщение от melky (Сообщение 226144)
Есть скрипты для разворачивания рекурсии в цикл ... я видел его, если мне не показалось, могу поискать, любопытная вещица.

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

Gozar 09.01.2013 01:09

Цитата:

Сообщение от melky
Презентация об оптимизации компилирования JavaScript в V8

Я бы скорее давал ссылку на JavaScript глазами JIT-компилятора, Вячеслав Егоров
Тем, кто не в курсе про jit будет гораздо более понятно, нежели текстовый срез из доклада.

Ну и линк на слайды: http://s3.mrale.ph/TechForum2012.pdf

melky 09.01.2013 18:00

Цитата:

Сообщение от Gozar (Сообщение 226154)
Я бы скорее давал ссылку на JavaScript глазами JIT-компилятора, Вячеслав Егоров
Тем, кто не в курсе про jit будет гораздо более понятно, нежели текстовый срез из доклада.

спасибо, поглядим.

godofjavascript 10.01.2013 05:20

Gozar,
спасибо

melky 10.01.2013 19:31

Чёртов виндовс... на линухе всё гладко - все мессаги выводятся в консоль! ;)

флаг --trace_inlining, например, рассказывает об заинлайненных функциях
Код:

Did not inline call called from type (target not inlineable).
Inlined type called from type.number.
Did not inline call called from type (target not inlineable).
Inlined type called from type.undefined.
Did not inline call called from type (target not inlineable).
Inlined type called from type.func.

исходники для них:
/**
     * Вернёт строковое представление типа аргумента.
     * При необходимости вернёт [[Class]] в нижнем регистре.
     *
     * @param {?} x
     * @return {string}
     */
    function type(x) {
        var type = typeof(x);
        if (type === "object") {
            type = Object.prototype.toString.call(x).slice(8, -1).toLowerCase();
        }
        return type;
    }

    /**
     * Проверит, является ли аргумент функцией.
     *
     * @param {?} x
     * @return {boolean}
     */
    type.func = function (x) {
        return type(x) === "function";
    };


Часовой пояс GMT +3, время: 15:00.