OctoLinker / OctoLinker

OctoLinker β€” Links together, what belongs together
https://octolinker.vercel.app
MIT License
5.28k stars 278 forks source link

A script in the extension "OctoLinker" is causing Firefox to slow down #557

Closed fregante closed 4 years ago

fregante commented 5 years ago

I consistently get this message when visiting this large file:

A script in the extension  OctoLinker  is causing Firefox to slow down

https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts

Admittedly this is more of an edge case, given the length of the file, but freezing the browser for a few seconds is never good.

Perhaps the operation can be done in the background and/or split up in multiple batches to avoid long blocking.

stefanbuck commented 5 years ago

Wow this file length is insane! I think it's best to not execute OctoLinker at all if a file goes beyond a certain line limit. Refactroing the extension to support such edgaces seems to be overkill and I would rather spend my time with new features.

Defining the limit is probaly the trickest task here. My gut feeling says that everyone beyone 5000 lines should be ignored.

How does this sound to you?

fregante commented 5 years ago

That sounds alright, but it might be a good opportunity to find some bottlenecks, like these long garbage collection events

Screenshot 2019-06-07 at 19 31 59

It appears to be caused by this (which contains getAggregateText): https://github.com/OctoLinker/OctoLinker/blob/0174f49ed52e936dadcacc81f8cdf428d86bc950/packages/helper-insert-link/index.js#L2

I'm sure that there's a more efficient way to replace dom elements, e.g. looping getTextNodes

Also from what I understand, the whole document is re-parsed for each regex

silverwind commented 5 years ago

I too regularily observer the browser tab hanging when opening moderately big package.json files. For example https://github.com/elastic/kibana/blob/master/package.json freezes my browser tab for almost 20 seconds, which I think is borderline unacceptable. A max line count won't help here as the density of links is just too high in such files.

If CPU-intensive work is to be done, maybe offload it to a web worker? Thought based on the comments above, the bottleneck sounds like inefficient parsing/replacing.

fregante commented 5 years ago

freezes my browser tab for almost 20 seconds, which I think is borderline unacceptable.

20-seconds freezes aren't just borderline unacceptable

Wow, that file is only 440 lines but freezes the browser for longer than TypeScript's 18K lines file

stefanbuck commented 5 years ago

So this issue isn't new for both of you and existed also in the previous version, right? Anyway, sorry for this bad experience.

@bfred-it I agree, there are more efficeient ways to replace dom elements, but maintaining this extension for many years, taught me that relying on classNames and dom elements is error-prone and time consuming to keep in sync. findandreplacedomtext was solving this issue for me pretty well.

OctoLinker does not apply all RegExps over and over again on the same document. First, all blobs on a docuemnt are paresed and stored in an unified format. Then based on the filepath and/or language information one or more plugins are applied to parse the blob. Usally it's only one apart from a few expections. A plugin can define one or more RegEx where each RegEx is applied on the blob, not the whole page. The JavaScript plugin contains three RegExp https://github.com/OctoLinker/OctoLinker/blob/master/packages/plugin-javascript/index.js#L108

Maybe this helps to track down the issue. I'll look into this, but feel free to add your ideas / explorations as well.

silverwind commented 5 years ago

Yes, this is not a new issue, it's been that way since I'm using the extension.

getAggregateText is here. It looks to extract a array of strings from the DOM. I think this is a operation that only needs to run once per page, maybe some memoization might help.

fregante commented 5 years ago

Does the operation need to be synchronous or can it pause every 200ms so the browser can go through the event loop?

In Refined GitHub we had the same issue but and it was resolved by adding an await setTimeout in the loop if the loop takes too long.

stefanbuck commented 5 years ago

That's a great suggestions, thanks. Might be slightly tricky to implement since all the dom wrapping is sync. Refactoring that to async is actually on my todo list since quite a while. I'll look into this later this week and maybe there is a shortcut.

stefanbuck commented 5 years ago

Yesterday, I found some time and it's really a tricky one. I've noticed that Chrome is struggling with DOM parsing too, but "just" for ~3 seconds. Anyway, as you spotted the issues is related to the findandreplacedomtext dependency. I need more time to work on a solution. Do you notice this issue most of the times "just" for package.json files or also for regular files (expect huge files with 20k). That would be super useful to know to find a good solution.

stefanbuck commented 5 years ago

I might have found a solution which reduces the execution time from ~25 seconds to 250ms on Firefox and from 2 seconds to 250ms on Chrome. It needs more testing to verify the results, but I'm optimistic that this will be fixed soon

stefanbuck commented 5 years ago

Quick update: Today I started to continue on this issue. During the summer break I had other stuff to do πŸ˜‰ However, the original approach seems to break a few other things so I decided to rewrite this part from scratch. I'll keep you posted.

stefanbuck commented 4 years ago

Another few months passed by without much progress on this, sorry for that. However, in the past couple of days I made good progress and I would like to share an early version with you. I haven't done any intensive testing, but it seems to work fine. In this version linking of code snippets in MD files and comments is removed.

  1. Download octolinker-5.2.2-an+fx.zip
  2. Rename file to octolinker-5.2.2-an+fx.xpi
  3. Firefox > Menu > Add-ons > Install Add-on from file

Enjoy and please share your feedback with me. Thank you

Source is available here and PR will follow soon

stefanbuck commented 4 years ago

The new parsing is on average 81% faster on Firefox and 28% on Chrome. For big files like https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts and https://github.com/elastic/kibana/blob/master/package.json Chrome is round about 86% faster and Firefox finally does not crash and finish parsing after a reasonable time of ~250ms πŸ˜„

stefanbuck commented 4 years ago

Great gift for the 6th anniversary of OctoLinker πŸŽ…

fregante commented 4 years ago

Great to hear! I’ll be testing it in January if you don’t merge it first

silverwind commented 4 years ago

Indeed, that beta seems much faster in Firefox. I'll be testing it.

stefanbuck commented 4 years ago

Know issues:

stefanbuck commented 4 years ago

I found a few more issues with linking in diff views. Fixing those requires some more refactoring, but I'm optimistic. Stay tuned!

fregante commented 4 years ago

What's the latest beta version I can try?

stefanbuck commented 4 years ago

Do you mind using this branch and building the extension yourself using npm run firefox-open – although this isn't using your default Firefox installation. I need to rebase this branch with master since I added some parts as a separate PR already.

fregante commented 4 years ago

I just tested that on the TypeScript file and found no difference, it makes the yellow bar appear for 8-9 seconds just like the Store version.

Testing method:

  1. disable extension
  2. load https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts
  3. enable extension (store) or load from about:debugging (beta)
    • Firefox will inject the extension on all tabs
  4. wait for the yellow bar to appear

Firefox 72 (Refined GitHub disabled)

stefanbuck commented 4 years ago

I was primarily using this file for testing which seems to be more realistic in terms of length https://github.com/elastic/kibana/blob/master/package.json however it should not freeze your browser. Maybe it's suitable to not run OctoLinker on such long files at all. What do you think?

fregante commented 4 years ago

Maybe it's suitable to not run OctoLinker on such long files at all.

You could limit the usefulness of OctoLinker if the only alternative is a many-seconds lockup, but that's always the last option.

stefanbuck commented 4 years ago

Imports are usually defined at the top of a file so might be tacking just the first 100 lines into account on such big files could be a workaround.

stefanbuck commented 4 years ago

Strange, I can't confirm that Firefox hangs when using the source from this dev branch. I tried both npm run firefox-open and about:debugging, but both works πŸ˜•

stefanbuck commented 4 years ago

However, I think cap OctoLinker to take only the first ~200 lines for files with more than 10.000 lines is legit restriction.

*number needs to be defined

stefanbuck commented 4 years ago

All remaining issues are fixed and I'm super happy to share a Pull Request with you https://github.com/OctoLinker/OctoLinker/pull/774. Unit and E2E tests are passing so I'm quite confident it works as intended.

@fregante @silverwind in case you want to give it a try your self, please do so. Thanks

stefanbuck commented 4 years ago

Finally fixed πŸ˜„ Will be part of the upcoming version which is scheduled for early next week.

fregante commented 4 years ago

Just in time for Chinese New Year πŸŽ‰ πŸ˜ƒ

silverwind commented 4 years ago

Nice work and explanation. I think it's a case of time complexity reduction from O(n log n) to O(n) so it will scale much better for big datasets.