|
| 1 | +--- |
| 2 | + layout: post |
| 3 | + title: Split Servo's script crate |
| 4 | + date: 2018-08-09 00:30:00 |
| 5 | + summary: Summary of separation Servo's script crate to smaller crates |
| 6 | + categories: |
| 7 | + --- |
| 8 | + |
| 9 | +## Introduction |
| 10 | + |
| 11 | +I am Peter Hrvola (retep007) [Twitter](https://twitter.com/retep007) [Github](https://github.com/retep007). During my GSoC project, I have been working on investigating the monolithic nature of Servo's script crate and prototyping separation to smaller crates. Our goal was to improve the use of resources during compilation. Current debug build consumes over 5GB of memory and takes 347s. |
| 12 | + |
| 13 | +Our solution is based on structures in script crate beeing generic over *TypeHolder* which contains concrete types. Testing shows significant improvement in memory consumption (~3.7GB) and time (231s). |
| 14 | + |
| 15 | +## The process |
| 16 | + |
| 17 | + For prototyping, we have been using two repositories. One with [minimal script crate](https://github.com/retep007/servo) which contains only a few WebIDLs. We used minimal script crate for investigating ideas before implementing them in full scale. The second repository contains [full servo fork](https://github.com/retep007/servo_1). |
| 18 | + |
| 19 | +We have started to work on project very early. During community bonding period we wanted to make a few small pull requests to separate some isolated parts of script crate. As it turned out, there is no low hanging fruit in script crate. We have quickly encountered many unexpected issues, so we continuously moved to the original plan. |
| 20 | + |
| 21 | +All original ideas for investigation can be found in [GSoC project proposal](https://storage.googleapis.com/summerofcode-prod.appspot.com/gsoc/core_project/doc/5703489939308544_1521985113_Servo__Prototype_ways_of_splitting_up_the_script_crate.pdf?Expires=1533631036&GoogleAccessId=summerofcode-prod%40appspot.gserviceaccount.com&Signature=YqnoZPNmnUH0nJypUnCWLD50wFqU8%2FnZOVO8ImlKmQY3RQM5d85Q5WFdOqABIUQIrhUTqo2clrVJcdG%2FFgZmSXC20ErWY4PV6FWSGwhuK3WgHBRAfjPn2iz4hdhwitnQdMCBpaL9bZRvvUJa%2BEZU1CxLLC8pwK6c8yF18YJKHddxuos7ackjhG7vLSuIvVvxfn419g3%2BEsfQPEpVOZbFugbXgGg2FBH7n7ZUeCj%2BTpEZrhnow7W%2Biu3pGinj536nA%2BqccssL5%2FdVyGHHsbEFXBoek%2B5IoyEqXA92Gh8ydp%2Fba%2Btv4CbxbRyzkasIrP2eemooyvv334WC7TOBetyUBw%3D%3D). However, during the first week of coding, we have experienced many issues and come up with something that is more or less combination of all three proposed ideas. |
| 22 | + |
| 23 | +The biggest problem that caused most of the troubles and pain is that Rust at the time of doing this project did not support generic consts [RFC](https://github.com/rust-lang/rust/issues/44580). |
| 24 | + |
| 25 | +After two months of solving errors, we have been finally able to compile full servo and take some measurements about which I talk later in this blog post. |
| 26 | + |
| 27 | +Original GSoC project assignment was to prototype ways of splitting script crate, but since results were quite promising and we had a month of coding left we started to prepare a PR to Servo master. During the prototyping period, we have made a few ugly hacks to speed up development. As a result, we had to modify CodeGen to generate generic Bindings and modify thread_local variables that needed to be generic. |
| 28 | + |
| 29 | +## How it works |
| 30 | + |
| 31 | +The final idea of separation is based on using the *TypeHolderTrait*. TypeHolderTrait is a trait with type parameters for every WebIDL interface that we want to move outside of the script crate. *TypeHolderTrait* itself is defined in script crate with all type parameter traits. However, it is implemented outside of the script crate so it can provide concrete types. TypeHolder enables us to use constructs like static methods in script crate. Later, we use TypeHolder as type parameter for structs and methods that require access to external types. *TypeHolderTrait* might look like: |
| 32 | + |
| 33 | +```rust |
| 34 | +trait TypeHolderTrait { |
| 35 | + type DomParser: DomParserTrait<Self> |
| 36 | +} |
| 37 | +
|
| 38 | +``` |
| 39 | + |
| 40 | +````rust |
| 41 | +trait DOMParserTrait<TH: TypeHolderTrait>: MutDomObject + IDLInterface + MallocSizeOf + JSTraceable + DOMParserMethods<TH> { |
| 42 | + fn Constructor(window: &Window<TH>) -> Fallible<DomRoot<TH::DOMParser>>; |
| 43 | +} |
| 44 | + |
| 45 | +```` |
| 46 | + |
| 47 | +IDLTraits usually have only a few functions. |
| 48 | + |
| 49 | +## Effects on Servo |
| 50 | + |
| 51 | +Modifications in final PR should have only minimal effects on Servo speed. However, the codebase has undergone big surgery 🏥. With about 12000 modified line. The most intrusive change is to use TypeHolder where it is required. It turns out that TypeHolder is needed in a lot of places. Leading cause of such a large number of changes is mainly *GlobalScope* which is used all around the codebase and needs to be generic. |
| 52 | + |
| 53 | +Due to lack of Rust support for generic static variables at many places we had to modify the code to use initialization methods to fill static variables. For example, in Bindings, we have added an *InitTypeHolded* function which replaces the content of mutable static variables with appropriate function calls. This pattern was used with the *build.rs* script and also thread locals. |
| 54 | + |
| 55 | +## Speeeeed 🚀 |
| 56 | + |
| 57 | +I have done testing on MacBook Pro 2015, High Sierra, 2,7 GHz i5 dual core, 16 GB RAM using `/usr/bin/time`, which shows maximal memory usage during compilation. |
| 58 | + |
| 59 | +Resources were measured in this way: |
| 60 | + |
| 61 | +1. compile full servo |
| 62 | +2. modify Attr.rs file and ServoParser.rs file (after separation they are in different crates) |
| 63 | +3. measure resources used to build a full servo |
| 64 | + |
| 65 | +| | Generic Servo | Servo without modification | |
| 66 | +| --------- | ------------- | -------------------------- | |
| 67 | +| RAM | 3.74GB | 5.1GB | |
| 68 | +| Real time | 231s | 347s | |
| 69 | + |
| 70 | +As we can see in the table above, resource usage during compilation has drastically changed. The main reason is that because of a large number of generic structures which postpone parts of compilation to later monomorphization. In future, actual separation of dom structs to upstream crates will lower this number even more. In the current version, only six dom_structs were moved outside the script crate. |
| 71 | + |
| 72 | +## Future work |
| 73 | + |
| 74 | +At the time of writing this post, there is still continuous work on modifying generic Servo before creating a PR. |
| 75 | + |
| 76 | +Thinks left to be done: |
| 77 | + |
| 78 | +1. Fix tests |
| 79 | +2. Performance optimization |
| 80 | +3. Polish PR |
| 81 | + |
| 82 | +## Important links |
| 83 | + |
| 84 | +- [Pull request to Servo](https://github.com/servo/servo/pull/21371) |
| 85 | +- [GSoC project](https://summerofcode.withgoogle.com/serve/5065009403527168/) |
| 86 | +- [Document with TypeHolder idea](https://paper.dropbox.com/doc/Script-Trait-types--AJwd82loCgoigvnx2NGK7G1mAQ-BKKNiTpqoTSvd502snFlu) |
| 87 | + |
| 88 | +## Conclusion |
| 89 | + |
| 90 | +Working on a project for two months without successful compilation and having compiler yelling that you have made 34174 mistakes is a bit scary. However, they say that the more mistakes you make, the more you learn. I guess I have made a lot of mistakes and I have learned a ton as we have constantly been pushing the Rust-lang to its limits in large Servo codebase. All in all, this was an awesome project, and I enjoyed it very much. |
| 91 | + |
| 92 | +I would like to thank my mentor [Josh Bowman-Matthews (jdm)](https://twitter.com/lastontheboat) for this opportunity. It was such a pleasure to work with him. |
0 commit comments