Давайте рассмотрим гипотетическую ситуацию. Допустим у нас есть стек серверов, которые, в любой момент времени, по своей внутренней прихоти, могут быть в состоянии «ok» или «down». Интерфейс сервера позволяет узнать только его имя и текущее состояние. Но как устроен сервер, и откуда он берется нам не известно, у нас нет к нему доступа. Пусть этот код будет конструктором для наших серверов:
var Server = function(name){ this.name = name; this.ping = function(){return Math.round(Math.random())? 'ok' : 'down';}; };
Допустим есть компонент, наблюдатель, который будет следить за вверенными ему серверами, и по первому требованию отдавать нам хеш таблицу состояний по всем серверам. А какой-то демон, постоянно, яростно теребит этот компонент, следя за состоянием серверов. Пусть это будет конструктор нашего наблюдателя:
var CasualObserver = function(){ var stack = []; this.add = function(server){ stack.push(server); return this; }; this.check = function(){ var hashTable = {}; for(var i = 0, ln = stack.length; i < ln; i++){ hashTable[stack[i].name] = stack[i].ping(); } return hashTable; } };
Допустим вы программист которому поручили этот код, и, вежливо, попросили его оптимизировать, причем сохранив существующий интерфейс.
Ну вот сидите вы, и пялитесь в эти 14 строчек, и единственная (я иронизирую) мысль которая вертится у вас в голове — «стоит ли for на while заменить, или всё-же не стоит?». Мысль эта одновременно правильная и неправильная (эдакая суперпозиция мысли). Правильная в том, что самая трудоёмкая операция тут — цикл. Неправильная в том, что нужно думать не о том как оптимизировать его, а о том как избавится от него. Зачем нам динамически создавать хеш таблицу, когда можно работать с уже готовой, и просто вызывать .check для каждого элемента?
this.check = function(){ return { stack[0].name : stack[0].ping(), stack[1].name : stack[1].ping(), stack[2].name : stack[2].ping() }; }
Вот так мы сразу же избавляемся от цикла. Конечно вы посмотрите на меня как на дурака, и скажите — «Ага, Вань. Но серверов то у нас может быть произвольное количество, а не просто три. Да и к тому-же в JS нет рефлекшенов.»
А вы знали что в Javascript возможна примитивная рефлексия? (знали? Ну тогда ступайте ниже) Да-да, JS позволяет изменять существующий код и создавать новый в прямо в рантайме!
var sum = new Function('a', 'b', 'return alert(a + b);'); sum(2, 3);
С тем-же успехом, и для тех же целей, можно использовать всеми ненавистный eval. Подробнее об этом уже писали в других постах на хабре.
Ага, вы неверное уже всё сами поняли? Конечно! Давайте создадим конструктор для нашего наблюдателя, который сам для себя создать метод check:
var SelfModifyObserver = function(){ var stack = []; this.add = function(server){ stack.push(server); var code = 'return {'; for(var i = 0, ln = stack.length; i < ln; i++){ code += stack[i].name + ':' + 'stack[' + i + '].ping(),'; } code += '};'; this.check = eval('(function(){' + code +'});'); return this; }; this.check = function(){return {};} };
Выглядит дико, я с вами полностью согласен. Но оно работает!
Давайте проверим, оправдал ли себя этот подход или нет. Напишем небольшую функцию которая добавит наблюдателю 25 серверов, по одному на каждую букву латинского алфавита, а потом посмотрим сколько раз за секунду он сможет сделать проверку по ним.
var benchmark = function(instance, note){ for(var i=65; i<=90; i++){ instance.add(new Server(String.fromCharCode(i))); } var stamp = new Date().getTime(); var iterations = 0; while(new Date().getTime() - stamp <= 1000){ instance.check(); iterations++; } console.log(iterations + ' iterations per second for ' + note); return true; };
Вынужден признать, что сейчас у меня нет возможности протестировать это в Node.js, но тесты в офисе показали что на моем сервере прирост в производительности для подобного решения был порядка 30%.
А это результат в chrome на моем ноуте:
Видите! Выжали 30% практически из пустого места!
Спасибо если вам было интересно.
Идею подхватил в одном из докладов на JSConf EU 2012.
ссылка на оригинал статьи http://habrahabr.ru/post/158403/
Добавить комментарий