This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

WIP: Filter cache - #8942

Closed
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache
Closed

WIP: Filter cache#8942
IgorMinar wants to merge 4 commits into
angular:masterfrom
IgorMinar:filter-cache

Conversation

@IgorMinar

Copy link
Copy Markdown
Contributor

use a cache to avoid recomputing filters during dirty-checking.

in the modified large-table benchmark delivers 3x speed improvement for number filter (apply duration), but makes the uppercase filter 20% slower

TODO:

  • more benchmarking
  • special case dates since they are not primitives but can be cheaply compared via getTime()
  • decide on whether to make the cache an opt-in or an opt-out

they pass without any modifactions.
BREAKING CHANGE: filters that have hidden state won't be recalculated if the hidden state changes
TODO: more info...
avoid hasOwnProperty calls and more
@IgorMinarIgorMinar added this to the 1.3.0-rc.2 milestone Sep 5, 2014
@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@tbosch, @vojtajina please take a look and provide preliminary feedback

@jbedard

Copy link
Copy Markdown
Contributor

Have you thought of doing this for all expressions instead of just filters? $parse could record the inputs of an expression (variables + function calls?) while parsing, then use something such as a $$watchDelegate that does $watchGroup on all the inputs of the expression. For expressions such as a == 1 || a == 2 || a == 3 it could only watch a and avoid the operators/constants being evaluated. I think this would reduce watch time of all expressions but would increase memory usage by storing all the old values...

@jbedard

Copy link
Copy Markdown
Contributor

... 5 hours later ... checkout jbedard@844d04c (note that this is branched off #8901) - it actually works pretty well for a quick poc of what I mentioned. All the watched expressions I've looked at are faster, and object/array literals (in my test) are 10x faster because they don't have to build an object each time (I assume that's the reason). Expressions with noop filters are also 2-3x faster just by avoiding the noop filter call... (see the linked PR for the benchmarks I'm using).

This avoids executing the expression by only executing them when their input changes (or the input value is non-primitive similar to your PR). If the input changes then the expression is fully evaluated which will contain a lot of duplicate evaluation of those inputs. This means that expressions with frequently changing inputs (rare?) or non-primitive inputs could actually be slower due to the double evaluation. But if $parse was refactored more and those input values can be passed directly into parse instead of reevaluating them then this might be well worth it...

Sorry if this is off topic or the wrong place to put this, but I've been thinking about this lately and this PR seems pretty similar...

Comment threadsrc/Angular.js

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed boolean here

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

@jbedard very cool. I need more time to digest your changes, but it looks promising.

I'm surprised that you were able to make noop filters faster. does this include simple expressions like a | noop? I find it hard to believe that any caching can be faster than executing the noop filter.

I need to dig more into your benchmarks

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

awesome work btw!

@jbedard

Copy link
Copy Markdown
Contributor

Normally the expression tree for a | noop is binaryFn + |op + getterFn + fnInvoke/argFns + noop, so reducing the watcher to just getterFn actually cuts out quite a bit...

@lgalfaso

Copy link
Copy Markdown
Contributor

Even when I like the general idea, if this is going to be an opt-out, then I would like it to be at the 1.3 before we get into the next RC. The reason is that if this is enabled by default, then it will break modules like https://github.com/lgalfaso/angular-dynamic-locale

@IgorMinar

Copy link
Copy Markdown
ContributorAuthor

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily, but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" notifications@github.com wrote:

Even when I like the general idea, if this is going to be an opt-out, then
I would like it to be at the 1.3 before we get into RC. The reason is that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).

@tbosch

Copy link
Copy Markdown
Contributor

Should we delay rc1 then until this is in? I would vote for that...

On Saturday, September 6, 2014, Igor Minar notifications@github.com wrote:

I know. If now unknown problems arise we need to get this or the general
expression caching in asap.

I previously thought that we wouldn't be able to implement this easily,
but
it turned out to be doable.
On Sep 6, 2014 10:16 AM, "Lucas Galfasó" <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Even when I like the general idea, if this is going to be an opt-out,
then
I would like it to be at the 1.3 before we get into RC. The reason is
that
if this is enabled by default, then it will break modules like
https://github.com/lgalfaso/angular-dynamic-locale


Reply to this email directly or view it on GitHub
#8942 (comment).


Reply to this email directly or view it on GitHub
#8942 (comment).

Comment threadsrc/ng/parse.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reading this wrong of if this is the first time the filter is executed and input is undefined then the result will be undefined and not the value from the filter?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

darn it Lucas, how do you always find these corner-cases. you rock!

@rubenv

Copy link
Copy Markdown

angular-gettext would certainly break because of this. Nonetheless, I'd also welcome the change (it makes).

The breakage for locale filters can easily be avoided if you provide a clear method to wipe $$filterCache globally (once) whenever the current language changes.

@OverZealous

Copy link
Copy Markdown

Would it make sense for a filter to have an opt-out defined on the filter function. This is just me brainstorming, but what about allowing a property to be defined on the function object, something like this:

module.filter('myfilter',function(){functionmyFilter(value){//...}myFilter.$useFilterCache=false;returnmyFilter;});

Then if a filter function has $useFilterCache defined and === to false, it is never cached. (Alternatively, invert the name and change it to something easier to check, like $noFilterCache = true.)

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

@IgorMinar@jbedard@lgalfaso@tbosch@rubenv@OverZealous@knalli@Narretz@mary-poppins