Tale about how we thoughtlessly used on the project the boilerplate with a whole ammunition load of dependencies that nobody used, how we then stumbled when support for the Edge was needed, and how we heroically repaired what we actually broke.
No, it is too long.
The tale that all browsers are attributes, and some especially.
One day, one of my colleagues turned to me with a proposal to think together about one very interesting task: the C3 demo (a library for displaying graphs) when copying to our AngularJS application became so slow when using Edge that it began to fully and completely comply with the saying "go quietly - you will continue. " Further from the customer, current and all future potential projects.
First thought - check in other browsers. Chrome and Firefox insisted that we are doing well.
So it's dependency. Started removing dependencies from angular.module (). After nothing there was nothing, our position was still as disastrous.
The next step was to use the built-in dev tools performance tracker. Unfortunately, for a long time we looked at the wrong place, analyzing again and again the time for calling various JS functions, until, quite desperately, we did not look at the processing time of the CSS. Began to understand. Immediately surprised that after cleaning the project, the size of the collected and minified CSS file was still impressive. It turned out that flex-attr library was connected to this project, which no one used and which just came by inheritance from the boilerplate.
What could this library do that it almost hung up the Edge for a couple of seconds?
Flex-attr is a set of CSS rules for easy working with flex directly from HTML. I first encountered this approach when I used Angular Material for my project. Afterwards, it was decided to say goodbye to Angular Material, but I liked the approach, and I made my fork, leaving only CSS and SCSS, porting it under LESS and adding the much needed ability to redefine and add new postfixes and screen sizes (breakpoints).
A colleague got this library for his project, using our internal boilerplate, which I personally added to it a little earlier. What kind of life is it if you don't regularly shoot yourself in the legs, isn't it? The main thing - not in the head and not from a shotgun like Cobain, but I still have time to 27, so you can not worry.
As follows from the description, this library is a CSS file with a large number of rules for attributes ([your-attribute]). After it was turned off, the project came to life and ran like an Easter bunny. However, the sediment remained. I wanted a benchmark to understand the depth of the problem (maybe, of course, because my life is empty and only work can somehow entertain, but we are cultured people and we will not assume such vulgar things?). I wanted answers. I wanted to understand how to rewrite this library so that it would cease to be one of the torture instruments of the Holy Inquisition.
A simple Python script was written to measure performance. Its task is to create a set of html files with different numbers of blocks and CSS rules for classes and attributes, and also insert JS code there to determine the execution time.
HTML file structure:
<!doctype html> <html> <head> <title>CSS Tag Selctor Test</title> </head> <body> <style> CSS goes here </style> <script type="text/javascript">var renderTime = Date.now();window.addEventListener("load", () => console.log(Date.now() - renderTime))</script> <div>0</div> <div>1</div> <div>2</div> ... <div>n</div> </body> </html>
Every div has either a class,
<div class="test-selector-0">0</div>
either attribute
<div test-selector-0>0</div>
Each CSS rule sets a background-color for the block. Only one rule corresponds to each block. If the number of rules is less than the number of blocks, they are applied again from the beginning. The load time (in ms) is determined by the load event and output to the console.
For the tests, a new virtual machine with Windows 10 Enterprise with 4GB of RAM and 2 cores was created (to be more precise, this is one physical core, but two threads of execution, thanks to Hyper Threading). Microsoft Edge 38.14393.0.0, Google Chrome 60.0.3112.101, Mozilla Firefox 55.0.2 browsers were used. Only one browser was opened at a time, in which only one tab was open. To avoid the influence of any system processes on the benchmark results, each test was run 10 times for each browser and a median value was taken. The exceptions were tests "10,000 blocks / 10,000 rules for attributes", "50,000 blocks / 10,000 rules for attributes" for Edge. Only 5 and 3 measurements were made, respectively, since I was just too lazy to wait for the page render for 5-6 minutes. In the future, I will operate only with median values. Full results can be found here .
Results for 100 CSS rules
Blocks | Chrome
(classes) | Firefox
(classes) | Edge
(classes) | Chrome
(attributes) | Firefox
(attributes) | Edge
(attributes) |
---|---|---|---|---|---|---|
100 | four | 16.5 | thirty | four | 18 | 23.5 |
1000 | 16.5 | 95.5 | 44 | nineteen | 125.5 | 139.5 |
10,000 | 437.5 | 653 | 382 | 452 | 585 | 1338.5 |
50,000 | 3489 | 2565 | 5176 | 3566.5 | 2865.5 | 7061 |
Results for 1000 CSS rules
Blocks | Chrome
(classes) | Firefox
(classes) | Edge
(classes) | Chrome
(attributes) | Firefox
(attributes) | Edge
(attributes) |
---|---|---|---|---|---|---|
100 | 12.5 | 20.5 | 16.5 | 17.5 | 38.5 | 128 |
1000 | 40.5 | 115.5 | 127.5 | 96.5 | 198.5 | 647.5 |
10,000 | 479 | 618.5 | 974 | 960 | 1242 | 4578 |
50,000 | 3719.5 | 2877.5 | 5358.5 | 5936 | 6582.5 | 26597.5 |
Results for 10,000 CSS rules
Blocks | Chrome
(classes) | Firefox
(classes) | Edge
(classes) | Chrome
(attributes) | Firefox
(attributes) | Edge
(attributes) |
---|---|---|---|---|---|---|
100 | 99 | 33 | 24 | 156.5 | 134.5 | 1034.5 |
1000 | 149 | 85 | 77.5 | 618 | 8760 | 5185.5 |
10,000 | 655 | 625.5 | 788 | 5466 | 11020.5 | 46623 |
50,000 | 3681.5 | 3183.5 | 4743.5 | 36779 | 50663.5 | 326838 |
Considering the fact that the library that served as the root cause of this study was connected to the original application, but not a single class was used, it was decided to conduct another test: to compare the performance for those cases when CSS rules are actually applied in HTML and when no (i.e. they will be in CSS, but no one will use them). To do this, the parameter was added - no_apply_css in the initial benchmark.
Results for 10,000 CSS rules (if nothing is indicated in brackets, then CSS rules were used in HTML)
Blocks | Chrome | Firefox | Edge | Chrome
(No CSS applied) | Firefox
(No CSS applied) | Edge
(No CSS applied) |
---|---|---|---|---|---|---|
100 | 156.5 | 134.5 | 1034.5 | 177 | 84.5 | 1213.5 |
1000 | 618 | 8760 | 5185.5 | 689 | 562 | 6037 |
10,000 | 5466 | 11020.5 | 46623 | 6700 | 5156 | 76401 |
50,000 | 36779 | 50663.5 | 326838 | 38750 | 34351 | 431700 |
As you can see, the performance of all browsers degrades when using attributes, starting with a specific CSS file size. Yet Edge distinguished himself and did not shame his reputation as the fastest browser on our blue ball called Earth. What to do with it? Do not use attributes to write your CSS. You should also look very carefully at the libraries that you connect: they can bring with them a pack of so undesirable rules for attributes. And as the last experiment showed, even if you don’t use a single rule for attribute on your pages, where you decide to display a huge list or table (leave the question “why?” Outside of this discussion), performance will still suffer. If for some reason the use of a large number of attribute selectors cannot be avoided, then it is worth considering to assemble them into a separate CSS file and load this file only if absolutely necessary. For myself, I decided to simply transfer flex-attr to classes and refactor existing projects when the work is complete.
If you are interested in looking at the experimental data in some other section, then copy the data and aggregate the data as you see fit.
UPD 08/21/2017: I sent a bug report to Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/13348719/
Source: https://habr.com/ru/post/335992/