Collection v4
Доделал 4-ю версию своего эпичного велосипеда:) Создал новый тред, т.к. по сравнению с прошлой версией было сделано оч много конфликтующих изменений.
https://github.com/kobezzza/Collection Что это и зачем: В JavaScript для массивов есть встроенные методы-итераторы, например, forEach, reduce и map и т.д. Их очень удобно использовать и они делают наш код нагляднее. Данная либа позволяет юзать все те же методы + дополнительные для любых типов данных, а не только для массивов. Также итераторы в Collection работают значительно быстрее нативных. Также в этой либе реализован простой интерфейс для работы с хранилищами данных: localStorage, sessionStorage, indexedDB. Итак: По сравнению с прошлой версией было выпилено много лишнего (теперь сжатая либа - это всего 11.3 килобайта). Теперь либа позиционируется как единый и очень навороченный API для функциональной работы с коллекциями данных, а не как блендер из кучи всего (как было раньше:) ). Реализован единый прозрачный API для работы с хранилищами данных (localStorage, sessionStorage, indexedDB). Реализован свой собственный JIT компилятор для итераторов, который позволяет увеличить до 10-ти раз производительность в сравнении с нативными методами во всех современных браузерах (применяемые оптимизации не делают работу за JIT VM JavaScript, а наоборот: дополняют те вещи, которые не могут быть оптимизированы нативным JIT). В древних браузерах этот эффект может быть ещё выше. Переработаны интерфейсы методов для более простой и удобной работы. Код переписан на ECMAScript6 (с транслятором) :) Парочка примеров: // Перебор элементов в обратном порядке начиная с 5-го $C(document.querySelectorAll('.foo')).forEach(function (el) { ... }, { reverse: true, startIndex: 5 }); // Вернуть массив всех чётных элементов исходного массива $C([1, 2, 3, 4]).get(function (el) { return el % 2 === 0; }); // Cоздать новый объект на основе исходного, // {a: 2, b: 4} $C({a: 4, b: 16}).map(Math.sqrt); // Загрузить коллекцию foo из локального хранилища $C().load('foo'); О найденых багах пишите на гитхаб, вопросы можно задавать тут:) ЗЫ: про отвратительную историю коммитов в курсе, буду исправляться :) |
Было бы интересно послушать про оптимизации)
|
Цитата:
Т.е. методы Collection принимают оч много параметров-ограничений, вроде количество элементов в ответе и т.д. Эта информация учитывается при генерации оптимизированный функции, т.е. если нет необходимости это учитывать то логика проверки не войдёт. В функциях callback учитывается количество принимаемых параметров, наличие this, наличие возвращаемого значения и т.д. - эта информация также помогает построить оптимизированную функцию. Анализируется тип данных, например для массиво-подобных объектов по умолчанию используется for, как и для массивов вместо for in. После нормальной реализации в браузерах for of добавлю дополнительную поддержку итераторов. Фильтры анализируются более глубоко, вплоть до возможного инлайнинга. Инлайнингу почти всегда подвергаются функции-строки (вроде :el > 2), а также лямбда-функции (в теле которых сразу идёт return), но только если пройдёт проверка внешних зависимостей. Поощряется разбивать фильтры на множество атомарных и затем использовать композицию, вроде: .get('odd && unique || some') // и т.д. подробнее в доке Т.к. составные фильтры перед выполнением строят дерево логики и оптимизируют его для максимального инлайнинга. После всех оптимизаций составляется специальный ключ, который определяет вид применённых оптимизаций и в дальнейшем этот ключ может использоваться для схожий операций, т.е. оптимизация делается один раз. В браузерах дополнительно оптимизированные функции подключаются как внешние скрипты, что дополнительно увеличивает скорость работы и улучшает отладку (для node планирую добавить кеширование во внешний файл). Также ключи оптимизации в браузере сохраняются в локальном хранилище и при обновлении странице будут подключены как внешний скрипт, т.е. уже не будет необходимости в повторной оптимизации, причём ключи привязываются к домену, т.е. при повторном заходе на сайт оптимизация будет ещё сильнее. На примере проекта над которым я сейчас работаю замена нативных методов на Collection без внесения дополнительных оптимизация увеличило скорость работы всего сайта в 12-16 раз во всех браузерах. ЗЫ: Вот пример сгенерированных ключей http://screencast.com/t/M4IqYUqRNF ЗЫЗЫ: Ещё один слой оптимизации - это оптимизация и сжатие либы в GCC Adv Mode :) Хз что он там колдует, но после сжатия всё реально шустрее работает :) |
Цитата:
не особо вникал в код пока, но я так понял ты вообще не используешь встроенные циклы типа for при переборе, или как ты перебираешь то что передали? К примеру я передал массив , как он будет обработан и каким циклов перебран? |
Цитата:
Цитата:
|
Цитата:
Цитата:
вопрос по продвинутом сжатию, к примеру есть код (function (undef) { var local_name = "cache_module_", v_name = local_name + "cache_version"; function cache(params) { this.storage = sessionStorage; this.isUse = false; if(params.isLocal) { this.storage = localStorage; } } cache.prototype.get = function (id) { if(!this.isUse) return; try{ var obj = this.storage.getItem(local_name + id) return JSON.parse(obj); } catch(ignore) {} }; cache.prototype.set = function(key, data) { if(!this.isUse) return; setTimeout(function() { this.storage.setItem(local_name + key, JSON.stringify(data)); }.bind(this), 0) }; cache.prototype.removeItem = function(key) { this.storage.removeItem(local_name + key); }; cache.prototype.clear = function () { var storage = this.storage, key; for(var i = 0; i < storage.length; i++) { key = storage.key(i); if(~key.indexOf(local_name)) storage.removeItem(key); } } cache.prototype.init = function(parent, initObj) { var v; if(arguments.length != 2 || initObj.cacheVersion === undef) return; if(v = this.get(v_name) && v != initObj.cacheVersion) { this.clear(); } this.set(v_name, initObj.cacheVersion); this.isUse = true; }; Object.defineProperty(cache.prototype, "name", { value: "cache", writable: false }); window.Cache_module = cache; }()); // почему из него убираються прототипы и остаеться (function(){function b(){}Object.defineProperty(b.prototype,"name",{value:"cache",writable:!1});window.a=b})(); это из за того что они явно не используеться в коде? |
Цитата:
Цитата:
|
Цитата:
Цитата:
|
Цитата:
|
Цитата:
|
Цитата:
(это официальная дока closure compiler) |
kobezzza, а почему не используешь объект для передачи параметров?
|
Цитата:
https://github.com/kobezzza/Collecti...%D 0%BE%D0%B2 Цитата:
{ filter, id, mult, count, from, startIndex, endIndex, reverse, inverseFilter, notOwn, live, useForIn, vars, context } т.е. использую. Для внутренних вызовов юзается вызов с перечислением параметров, т.к. это работает быстрее, а для конечного использования конечно можно использовать объекты-параметры, если нужно. |
Цитата:
Plovr тоже ничё так :) |
kobezzza, не могу понять как сделать что класс не переименовывало, пробывал ставить @constructor но не помогает
(function () { /** @constructor - пробывал так */ function constr() {// сохранить имя constr } window.Constr = constr }()) Цитата:
|
Цитата:
Во первых: http://javascript.ru/optimize/google...d-optimization Во вторых: @expose https://developers.google.com/closur...s-for-compiler Ну и в третьих просто глянул бы, как сделано у меня: https://github.com/kobezzza/Collecti.../core/core.jsn (this.Collection; и this.$C; - это для поддержки автокомплита в WebStorm) |
Цитата:
|
Цитата:
вот как у тебя, так и надо. (про строковые имена): global['Collection'] |
Цитата:
Либа жмётся и нужные методы внешние, а все "издержки" 100% закрываются gzip, т.е. нет никакой разницы. Если я жму либу вместе с проектом, то пред сжатием я дополнительно вырезаю @expose (replace(/@expose/gm, '') в скрипте сборки) и всё (или написать файл экстерн, но это даст хуже сжатие). А вот если я юзаю экспорт как ['...'], то при сжатии с проектом уже без файла экстерна не обойтись, и сжатие будет хуже. *** Если я пишу ['...'] то это означает что всегда-всегда это свойство должно быть с таким именем. Вот пример: https://github.com/kobezzza/Collecti...rs.storage.jsn Свойство Collection.Drivers.Storage должно быть доступно при использовании либы как "внешнего скрипта", но при сжатии вместе с проектом должно сжиматься, поэтому я юзаю @expose. А свойства 'indexedDB', 'localStorage' и т.д. должны быть такими всегда, поэтому я экспортирую их в кавычках. Например: global['Collection'] сделан так не спроста, т.к. Collection фигурирует в генерируемых скриптах в рантайме, т.е. всегда должны быть именно таким. Разумеется я читал: Цитата:
|
kobezzza,
![]() Генерирует, по-моему, такой код: goog.exportProperty( %OBJECT% , "%PROP_NAME%", %PROP_VAL%); Исходник: /** * Exports a property unobfuscated into the object's namespace. * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction); * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); * @param {Object} object Object whose static property is being exported. * @param {string} publicName Unobfuscated name to export. * @param {*} symbol Object the name should point to. */ goog.exportProperty = function(object, publicName, symbol) { object[publicName] = symbol; }; Использование: /** @export */ MYOBJ.MYPROP = 'FOO' Цитата:
|
melky, я в курсе про это, только где аргументы против @expose ?:) Юзать его можно, юзать его удобно, но просто когда его юзаешь нужно думать. Иначе бы его просто не было:)
|
Цитата:
Вид хранилища у меня задаётся как Storage.lib = '...' поэтому нужно такое эскпортирование. |
Цитата:
Y.prototype.t = function(a) { /* A CODE */ }; Y.prototype.FOO= Y.prototype.t; @expose : Y.prototype.FOO = function(a) { /* A CODE */ }; разница - в использовании. при экспорте использование - это Y.t, при expose - Y.FOO Ну и исчо при компилировании некоторые методы выносятся в функции. У меня был метод без аругментов. Obj.foo = function () { alert(this.bar) } // Используется так : Obj.foo(); Теперь он стал такой функцией : function foo (a) { alert(a.bar) } // Используется так : foo(this); Хотя как и экспорты, expose эту фишку убивает (проверил сейчас). Наверное, наш спор похож на "табы VS пробелы" и похожие, но у меня плохое предчувствие насчёт @expose, поэтому и не юзаю :) |
kobezzza, вся документация находится в Readme.md ?
|
Цитата:
|
Жаль, экстернов нет :( нипосжимать.
Попробую пощупать либу в след. проекте. Кстати, хотел вот сделать pull request на unrolling loops, но не смог разобраться в исходниках и коммитах :Р |
Цитата:
Кстати можно сделать свою сборку либы: node build А в файле build.jsn можно выкинуть не нужное. Цитата:
А в исходниках что не понятно? Либа лежит в папке lib, итерационные методы в папке mult, все методы имеют jsdoc и описание. Основные файлы имеют расширение .jsn (а .js - это файлы сгенериные транслятором). Развёртка циклов профита не даёт, т.к. современные JS VM в этом плане оч прокачались и сами всё делают, я пробывал, но потом выпилил за ненадобностью:) Но копать тут: https://github.com/kobezzza/Collecti...t/compiler.jsn |
Цитата:
|
Цитата:
Исходники в папке /lib/, сборка в папке /build/ (там также есть сжатая версия) |
в форме офтопа
kobezzza, не могу понять - либа тебе нужна по основной работе? Или тебе хватает энтузиазма делать её вместе с докой просто для себя, обучения и с призрачной надеждой что она станет популярной? Вы тут такой жути понаписали про оптимизацию что я могу только догадываться о количестве потраченных человеко-часов. Уже прежней версии нет, сайт сгинул, так ещё раз всё с нуля на гитхабе теперь написано...
Лично я в одном проекте уже третью версию использовал, сейчас буду применять четвёртую во втором. |
Цитата:
Про связь с прошлой версией: я не изменил ни концепцию ни структуру, а просто выкинул те вещи, которые не показали своей полезности на практике, т.е. с большой долей вероятности переход будет простым. Были конечно некоторые дизайнерские изменения в интерфейсах, но их не очень много. Основные ломающие изменения: 1) Был выкинут кластер шаблонизации, т.к. он эволюционировал в отдельный мой проект Snakeskin. 2) Был убран SQL-парсер, т.к. на практике применять его у меня как то не очень вышло. 3) Выкинуты методы: stat, groupStat, copy, move, т.к. их легко создать на основе get/map/reduce/ 4) Выкинуты некоторые вспомогательные методы, вроде resetCollection, вряд ли их кто использовал 5) push_ методы переименованы в add_. Т.е. основная цель версии 4 была уменьшить кодовую базу и добиться высокой скорости создания/выполнения. Теперь про сайт: по хорошему нужно создать сайт-описание на гитхабе, а домен collection-js.com привязать к нему, но у меня правда нет на это времени, поэтому я его прикрыл пока, т.к. там содержится устаревшая дока и написал полностью новую доку на гитхабе. Если честно не вижу особых проблем с этим, т.к. какая разница где лежит дока. Пиши вопросы: я всегда оперативно на них отвечаю. |
Выпустил обновление 4.0.3 с исправлениями некоторых найденых ошибок.
|
Цитата:
|
Цитата:
ЗЫ: чёто у мя сёня день релизов :) запил 4.1.0, где были улучшены интерфейсы методов indexOf, lastIndexOf, _drop. |
kobezzza, хотел спросить насчет реализации forEach, по сути этот метод только перебирает массивы и только по числовым индексам (без использования Object.keys или for in), почему он тогда медленнее подобной реализации (в хроме критически медленне) и что может не так быть в этой реализации?
function each(arr, callback, context) { for(var i = 0, leng = arr.length; i < leng; i++) { if(context) callback.call(context, arr[i], i, arr); else callback(arr[i], i, arr); } } сделал тест ради интереса http://jsperf.com/each-test-speed |
Цитата:
На самом деле нельзя сказать точно: медленнее или нет, т.к. в некоторых случаях JIT VM может эффективно скомпилить тот или иной код, а иногда нет. Лучше всех на данный момент с такими оптимизациями справляется ИЕ11. Но в большинстве случаев падение скорости в нативных методах связано с тем, что стандартные массивы в JS - это на самом деле не массивы, а просто сахар над хешом. Цитата:
Но на самом деле forEach не то место, где нужно смотреть, т.к. он быстрый сейчас везде. Большая разница в производительности на всяких выборках (всякие map, filter и т.д.). |
Цитата:
|
Цитата:
Посмотри тесты, что я тебе привёл по ссылке: https://github.com/kobezzza/Collecti...r/forEach.html сравнения forEach https://github.com/kobezzza/Collecti...filer/get.html get vs filter https://github.com/kobezzza/Collecti...WithCycle.html get vs filter внутри цикла Чтобы склонить репозитарий git clone [url]https://github.com/kobezzza/Collection[/url] |
сделал тест
Цитата:
|
Цитата:
http://screencast.com/t/kTWjV15n5 вот результат замеров в 3-х браузерах на моей машинке. Хотя повторюсь, что эти тесты все искусственные. |
Часовой пояс GMT +3, время: 06:38. |