Skip to content

Refactor the escape() function to improve performance 10-20% - #975

Merged
styfle merged 1 commit into
markedjs:masterfrom
KostyaTretyak:renew-escape
Sep 11, 2018
Merged

Refactor the escape() function to improve performance 10-20%#975
styfle merged 1 commit into
markedjs:masterfrom
KostyaTretyak:renew-escape

Conversation

@KostyaTretyak

Copy link
Copy Markdown
Contributor

No description provided.

This was referenced Dec 21, 2017
Merged
@joshbrucejoshbruce added this to the 0.5.0 - Architecture and extensibility milestone Dec 25, 2017
@Feder1co5oave

Copy link
Copy Markdown
Contributor

I know this sounds kinda silly, but can we stick to the present coding style? This almost looks like a different language.

@KostyaTretyak

KostyaTretyak commented Dec 27, 2017

Copy link
Copy Markdown
ContributorAuthor

Okey, I changed the style code, it provided me with VS Code through auto formatting. Also, I replaced const to var. Hope it's OK.

Comment threadlib/marked.js Outdated
"'": '''
};

var escapeTestNoEncode = /(?:[<>"']|&(?!#?\w+;))/;

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.

There's no need to use grouping to wrap the whole thing

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.

Thank you, I fixed it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Y'all are awesome! Thank you.

Comment threadlib/marked.js Outdated
* Helpers
*/

var escapeTest = /[&<>"']/;

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.

These should be declared inside escape() IMO

@KostyaTretyakKostyaTretyakJan 3, 2018

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.

No, marked do not have to recreate the same RegExp instance every call escape(). This reduces performance and increases memory usage.

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.

Ok, I made them static.

@Feder1co5oave

Copy link
Copy Markdown
Contributor

Actually, I've found there's no advantage in testing before replacing, you still have to scan the whole thing at least once, either by testing or replacing, so the first phase is pretty useless.

# with current changes:
$ node test --bench
marked completed in 8388ms.
marked (gfm) completed in 9380ms.
marked (pedantic) completed in 8315ms.
Could not bench robotskirt.
Could not bench showdown.
Could not bench markdown.js.
# without testing first:
$ node test --bench
marked completed in 8394ms.
marked (gfm) completed in 9286ms.
marked (pedantic) completed in 8045ms.
Could not bench robotskirt.
Could not bench showdown.
Could not bench markdown.js.

And you can spare some line of code:

functionescape(html,encode){if(encode){returnhtml.replace(escape.replace,function(ch){returnescape.replacements[ch];});}else{returnhtml.replace(escape.replaceNoEncode,function(ch){returnescape.replacements[ch];});}}escape.replace=/[&<>"']/g;escape.replaceNoEncode=/[<>"']|&(?!#?\w+;)/g;escape.replacements={'&': '&amp;','<': '&lt;','>': '&gt;','"': '&quot;',"'": '&#39;'};

@KostyaTretyak

Copy link
Copy Markdown
ContributorAuthor

My first escape() without regexp.test():

functionescape(html,encode){if(encode){returnhtml.replace(escape.escapeReplace,function(ch){returnescape.replacements[ch]});}else{returnhtml.replace(escape.escapeReplaceNoEncode,function(ch){returnescape.replacements[ch]});}returnhtml;}

I run this code:

node test -t

Three times:

marked completed in 4146ms
marked completed in 4133ms
marked completed in 4125ms

My second escape() with regexp.test():

functionescape(html,encode){if(encode){if(escape.escapeTest.test(html)){returnhtml.replace(escape.escapeReplace,function(ch){returnescape.replacements[ch]});}}else{if(escape.escapeTestNoEncode.test(html)){returnhtml.replace(escape.escapeReplaceNoEncode,function(ch){returnescape.replacements[ch]});}}returnhtml;}

Run three times:

marked completed in 3885ms
marked completed in 3902ms
marked completed in 3893ms

@UziTech

Copy link
Copy Markdown
Member

Looks like this would make marked faster than markdown-it in our benchmarks

marked completed in 2200ms.
marked (gfm) completed in 2225ms.
marked (pedantic) completed in 1917ms.
showdown (reuse converter) completed in 21693ms.
showdown (new converter) completed in 22802ms.
markdown-it completed in 3275ms.
markdown.js completed in 11650ms.

@KostyaTretyak

KostyaTretyak commented Jan 7, 2018

Copy link
Copy Markdown
ContributorAuthor

In my benchmarks, remarkable is faster and more economical than remarked-it, but both of them can compete with marked when it is necessary to parse large files - more than 2 MB.

@joshbruce

Copy link
Copy Markdown
Member

Yeah. @worker8's independent benchmark sample has remarkable at the top as well.

@KostyaTretyak: Just to make sure. They can compete with marked with large files >2mb - versus the can not?

I think if we do what in #746 - we will be able to see areas for optimization easier. Right now we kinda have the large class thing happening.

@KostyaTretyak

Copy link
Copy Markdown
ContributorAuthor

In markdown.js and showdown, a very noticeable regress when files get larger than 300 KB.

@joshbruce

Copy link
Copy Markdown
Member

Interesting. Of course, if they're (or we're) targeting web developers - most folks aren't going to need to go above that. Maybe marked is the "large file" parser. :)

Thinking of something like LeanPub - parse an entire book in markdown.

@KostyaTretyak

Copy link
Copy Markdown
ContributorAuthor

Maybe marked is the "large file" parser. :)

No, it is a favorite when files are smaller than 2MB. If the files are bigger, then remarkable and markdown-it more faster.

Not for the sake of advertising, just for you to see it clearly. Do the following:

git clone https://github.com/KostyaTretyak/marked-ts.git
cd marked-ts
npm install
npm run compile

And then you can:

npm run bench -- -l 1000

Where -l 1000 - bench 1000 KB file with markdown tests. Here two dash from the front are necessary.

@joshbruce

Copy link
Copy Markdown
Member

Thanks! That's an interesting trick...might interesting for us to add to the CLI...if I'm understanding correctly:

I can secify how large of a file. Kind of like lipsum https://lipsum.lipsum.com - generate Markdown of X size to run the bench against.

@joshbrucejoshbruce removed this from the 0.5.0 - Architecture and extensibility milestone Apr 4, 2018

@styflestyfle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM 👍

@styfle

Copy link
Copy Markdown
Member

Is there a way to force push to invoke travis unit tests?

@UziTech

Copy link
Copy Markdown
Member

@KostyaTretyak if you can rebase this PR we should be able to merge it.

git fetch upstream && git rebase upstream/master && git push -f

@UziTech

Copy link
Copy Markdown
Member

I rebased and tested locally, and everything worked fine.

@styflestyfle changed the title Refactoring the old escape() function improved performance on 30-40%Refactor the escape() function to improve performance 30-40%Sep 11, 2018
@styfle

Copy link
Copy Markdown
Member

Nice! I ran benchmarks locally and this is the before and after:

Before

$ node test --bench
marked completed in 9146ms.
marked (gfm) completed in 11382ms.
marked (pedantic) completed in 8912ms.
commonmark completed in 10832ms.
markdown-it completed in 9832ms.
markdown.js completed in 29112ms.

After

$ node test --bench
marked completed in 8033ms.
marked (gfm) completed in 9946ms.
marked (pedantic) completed in 7905ms.
commonmark completed in 10897ms.
markdown-it completed in 9473ms.
markdown.js completed in 28330ms.

@KostyaTretyak

Copy link
Copy Markdown
ContributorAuthor

@UziTech, done:

git fetch upstream && git rebase upstream/master && git push -f

@styfle

Copy link
Copy Markdown
Member

@KostyaTretyak Thanks! Can you fix this lint error 😄

@styflestyfle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Excellent! 🎉

@UziTechUziTech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome work @KostyaTretyak

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@KostyaTretyak@Feder1co5oave@UziTech@joshbruce@styfle