Closed
Bug 1397626
Opened 7 years ago
Closed 7 years ago
stylo: Consider creating refcounted wrapper object for FontFamilyList and store it in specified value so that it can be shared between style structs
Categories
(Core :: CSS Parsing and Computation, enhancement, P3)
Core
CSS Parsing and Computation
Tracking
()
RESOLVED
FIXED
mozilla58
People
(Reporter: xidorn, Assigned: heycam)
References
(Blocks 1 open bug)
Details
Attachments
(3 files, 1 obsolete file)
Because of insufficient sharing of font struct, it seems that array buffer for family name list shows up to be a significant part in memory usage (see bug 1397380 comment 8). We may want to use a refcounted wrapper around FontFamilyList, and store a pointer to it in specified value, so that it can be shared between style structs.
Assignee | ||
Updated•7 years ago
|
Assignee: nobody → cam
Status: NEW → ASSIGNED
Assignee | ||
Comment 1•7 years ago
|
||
I have a WIP patch that adds a refcounted wrapper to FontFamilyList::mFontlist (the nsTArray), since that ended up being easier than making the FontFamilyList itself refcounted, just due to the generic fixups we do after computing font-family. On the HTML spec, I get ~9000 FontFamilyList objects, and with the patch, only 419 unique refcounted mFontlist objects. With some instrumenting it looks like this will save around 730 KB.
Assignee | ||
Comment 2•7 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=df8256bafd3c38c1f7364d23d207ddb761b12a7f
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Comment 5•7 years ago
|
||
mozreview-review |
Comment on attachment 8913611 [details] style: Use a SharedFontlist object to store font-family values for Gecko. https://reviewboard.mozilla.org/r/184996/#review190182 ::: servo/components/style/properties/longhand/font.mako.rs:386 (Diff revision 1) > + FontFamilyList(families) > + } > + > + #[cfg(feature = "gecko")] > + pub fn new(families: Vec<FontFamily>) -> FontFamilyList { > + let mut names: structs::nsTArray<structs::FontFamilyName>; I would be much more comfortable if this wasn't on the stack from the start. Can we instead have the Construct call construct a RefPtr for us? ::: servo/components/style/properties/longhand/font.mako.rs:389 (Diff revision 1) > + #[cfg(feature = "gecko")] > + pub fn new(families: Vec<FontFamily>) -> FontFamilyList { > + let mut names: structs::nsTArray<structs::FontFamilyName>; > + unsafe { > + names = mem::uninitialized(); > + bindings::Gecko_nsTArray_FontFamilyName_Construct(&mut names); It's best if the construct call uses its knowledge of the length of the vec to preallocate. It also means we can do the AppendFoo foo stuff in pure Rust; if the array is preallocated with its length already set to `families.len()`. But this isn't necessary. ::: servo/components/style/properties/longhand/font.mako.rs:417 (Diff revision 1) > + } > + } > + } > + > + unsafe { > + let ptr = bindings::Gecko_SharedFontlist_Create(&mut names); I don't really like this "move" here, but this will go away once we create the `RefPtr<SharedFontList>` off the bat and write to it directly ::: servo/components/style/properties/longhand/font.mako.rs:2364 (Diff revision 1) > cx.style().get_font().gecko(), > cx.device().pres_context() > ) > } > - let family = system.fontlist.mFontlist.iter().map(|font| { > + let names = unsafe { &system.fontlist.mFontlist.mRawPtr.as_ref().unwrap().mNames }; > + let family = names.iter().map(|font| { can't we just addref this in gecko mode?
Attachment #8913611 -
Flags: review?(manishearth) → review+
Assignee | ||
Comment 6•7 years ago
|
||
mozreview-review-reply |
Comment on attachment 8913611 [details] style: Use a SharedFontlist object to store font-family values for Gecko. https://reviewboard.mozilla.org/r/184996/#review190182 > I would be much more comfortable if this wasn't on the stack from the start. > > Can we instead have the Construct call construct a RefPtr for us? The model I chose on the C++ side was that a SharedFontlist object is immutable, and constructed with an array of FontFamilyName objects that it steals the contents of. (So that we don't have the ability to change the object when it's being shared.) I could have some other way to build up list of FontFamilyNames to then pass to Gecko_SharedFontlist_Create, e.g. just have a Vec, then I call an FFI function to in-place construct each element (since they're non-POD), and then pass a pointer/length, and the SharedFontlist clones them all. But I think it's slightly more straightforward to just use the nsTArray. > It's best if the construct call uses its knowledge of the length of the vec to preallocate. > > It also means we can do the AppendFoo foo stuff in pure Rust; if the array is preallocated with its length already set to `families.len()`. But this isn't necessary. But can we do the AppendFoo things from Rust? I didn't think we could construct a new nsString from the Rust side. > can't we just addref this in gecko mode? Yes, good point!
Reporter | ||
Comment 7•7 years ago
|
||
mozreview-review |
Comment on attachment 8913611 [details] style: Use a SharedFontlist object to store font-family values for Gecko. https://reviewboard.mozilla.org/r/184996/#review190276 ::: servo/components/style/properties/longhand/font.mako.rs:387 (Diff revision 1) > + unsafe { > + names = mem::uninitialized(); > + bindings::Gecko_nsTArray_FontFamilyName_Construct(&mut names); > + } I don't really like this either. Since empty `nsTArray`s are basically just a pointer to `nsTArrayHeader::sEmptyHdr`, I think you can create `new()` method for `nsTArray` in gecko_bindings/sugar. That way we would not need this `unsafe` block anymore, as well as the `Gecko_nsTArray_FontFamilyName_Construct` FFI function. ::: servo/components/style/properties/longhand/font.mako.rs:418 (Diff revision 1) > + } > + } > + > + unsafe { > + let ptr = bindings::Gecko_SharedFontlist_Create(&mut names); > + bindings::Gecko_nsTArray_FontFamilyName_Destroy(&mut names); I don't really like this function either... Probably we can add `Drop` impl for `nsTArray` in sugar, and `assert!` (or at least `debug_assert!`) that it is referring to `sEmptyHdr` so that we don't leak anything.
Reporter | ||
Comment 8•7 years ago
|
||
mozreview-review |
Comment on attachment 8913610 [details] Bug 1397626 - Part 1: Add a SharedFontList class. https://reviewboard.mozilla.org/r/184994/#review190262 In general, I think the idea is good. There are some non-trivial changes I'd like to see, so cancel r? for now. ::: gfx/thebes/gfxFontFamilyList.h:188 (Diff revision 1) > + explicit SharedFontlist(const nsAString& aFamilyName, > + QuotedName aQuoted) I don't think you need `explicit` here... Gecko's lint only require `explicit` for constructors which can be used with a single parameter. I guess it isn't harmful either, though. ::: gfx/thebes/gfxFontFamilyList.h:200 (Diff revision 1) > + : mNames { aName } > + { > + } > + > + explicit SharedFontlist(nsTArray<FontFamilyName>&& aNames) > + : mNames(aNames) You need `Move(aNames)` here, otherwise you would be calling copy constructor. ::: gfx/thebes/gfxFontFamilyList.h:204 (Diff revision 1) > + size_t SizeOfIncludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const > + { > + size_t n = 0; > + if (mRefCnt.get() == 1) { > + n += aMallocSizeOf(this); > + n += mNames.ShallowSizeOfExcludingThis(aMallocSizeOf); > + for (const FontFamilyName& name : mNames) { > + n += name.SizeOfExcludingThis(aMallocSizeOf); > + } > + } > + return n; > + } IIUC, this is shared between specified value and computed value for Servo backend, and it is never shared for Gecko backend? In that case, I guess we can just add `SizeOfIncludingThis`, and call it in specified value for Servo, and computed value for Gecko? Oh, hmmm, checking backend is probably hard for measurement of computed value. Probably both `SizeOfIncludingThis` and `SizeOfIncludingThisIfUnshared`, and call the former for specified value in Servo, and latter for computed value? My main concern is that most of font list which is used would be shared between computed value and specified value, so that could become a significant part of unreported memory usage for Stylo. ::: gfx/thebes/gfxFontFamilyList.h:231 (Diff revision 1) > + > +/** > * font family list, array of font families and a default font type. > * font family names are either named strings or generics. the default > * font type is used to preserve the variable font fallback behavior > */ As far as you are here, probably consider remove the trailing space of this line. ::: gfx/thebes/gfxFontFamilyList.h:333 (Diff revision 1) > - mFontlist.RemoveElementAt(i); > - mFontlist.InsertElementAt(0, name); > + nsTArray<FontFamilyName> names; > + names.AppendElements(mFontlist->mNames); > + names.RemoveElementAt(i); > + names.InsertElementAt(0, name); > + SetFontlist(Move(names)); Hmmm, this feels a bit painful for users who don't use document fonts... But to change this situation, we may need some non-trivial refactor... I guess it is fine for now :/ ::: layout/style/ServoBindings.h:297 (Diff revision 1) > +void Gecko_nsTArray_FontFamilyName_Construct(nsTArray<FontFamilyName>* names); > +void Gecko_nsTArray_FontFamilyName_AppendNamed(nsTArray<FontFamilyName>* names, nsIAtom* name, bool quoted); > +void Gecko_nsTArray_FontFamilyName_AppendGeneric(nsTArray<FontFamilyName>* names, FontFamilyType type); > +void Gecko_nsTArray_FontFamilyName_Destroy(nsTArray<FontFamilyName>* names); > +void Gecko_FontFamilyList_SetFontlist(FontFamilyList* list, nsTArray<FontFamilyName>* names); > +SharedFontlist* Gecko_SharedFontlist_Create(nsTArray<FontFamilyName>* names); Please comment here whether the returned value is addrefed or not. Also it seems you are moving the contents from `names`. You should probably document that in comment as well. ::: layout/style/ServoBindings.cpp:1259 (Diff revision 1) > void > -Gecko_FontFamilyList_Clear(FontFamilyList* aList) { > - aList->Clear(); > +Gecko_nsTArray_FontFamilyName_Construct(nsTArray<FontFamilyName>* aNames) > +{ > + new (aNames) nsTArray<FontFamilyName>(); > +} > + > +void > +Gecko_nsTArray_FontFamilyName_Destroy(nsTArray<FontFamilyName>* aNames) > +{ > + aNames->~nsTArray(); > } Conceptually I don't really like these two methods... It would be great if we can remove them. See my comment 7. ::: layout/style/ServoBindings.cpp:1292 (Diff revision 1) > void > +Gecko_FontFamilyList_SetFontlist(FontFamilyList* aList, > + nsTArray<FontFamilyName>* aNames) > +{ > + aList->SetFontlist(Move(*aNames)); > +} I don't see you're using this function in the second part? ::: layout/style/nsCSSValue.h:339 (Diff revision 1) > class FontFamilyListRefCnt final : public FontFamilyList { > public: > - FontFamilyListRefCnt() > - : FontFamilyList() > + FontFamilyListRefCnt(nsTArray<FontFamilyName>&& aNames) > + : FontFamilyList(Move(aNames)) > - {} > - > - explicit FontFamilyListRefCnt(FontFamilyType aGenericType) > - : FontFamilyList(aGenericType) > - {} > - > - FontFamilyListRefCnt(const nsAString& aFamilyName, > - QuotedName aQuoted) > - : FontFamilyList(aFamilyName, aQuoted) > - {} > - > - FontFamilyListRefCnt(const FontFamilyListRefCnt& aOther) > - : FontFamilyList(aOther) > {} > > NS_INLINE_DECL_REFCOUNTING(FontFamilyListRefCnt); > > private: > ~FontFamilyListRefCnt() {} > }; It... seems to me that we should just replace `FontFamilyListRefCnt` with the new `SharedFontList` everywhere... I don't see how the extra `mDefaultFontType` field makes any sense for any of the users of `FontFamilyListRefCnt`... Could you add a patch before this one for creating `SharedFontList` and use it to replace `FontFamilyListRefCnt`?
Attachment #8913610 -
Flags: review?(xidorn+moz)
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Assignee | ||
Comment 13•7 years ago
|
||
(In reply to Manish Goregaokar [:manishearth] from comment #5) > Can we instead have the Construct call construct a RefPtr for us? I decided to do this in the end and made some other changes, can you re-review?
Flags: needinfo?(manishearth)
Reporter | ||
Comment 14•7 years ago
|
||
mozreview-review |
Comment on attachment 8913610 [details] Bug 1397626 - Part 1: Add a SharedFontList class. https://reviewboard.mozilla.org/r/184994/#review190402
Attachment #8913610 -
Flags: review?(xidorn+moz) → review+
Assignee | ||
Comment 15•7 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=56e3f97342471b1b46b9e247031a77b6e70d493b
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Assignee | ||
Comment 20•7 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=24d5bba63c51ac0109ed106d3ae27a7fda7f352a
Reporter | ||
Comment 22•7 years ago
|
||
mozreview-review |
Comment on attachment 8913610 [details] Bug 1397626 - Part 1: Add a SharedFontList class. https://reviewboard.mozilla.org/r/184994/#review190824 ::: gfx/thebes/gfxFontFamilyList.h:174 (Diff revision 3) > /** > + * A refcounted array of FontFamilyNames. We use this to store the specified > + * value (in Servo) and the computed value (in both Gecko and Servo) of the > + * font-family property. > + */ > +class SharedFontlist Probably `SharedFontList` (uppercase `L`) instead? It seems to me although there are variables called `fontlist`, types are consistently using `FontList`. Probably even better `SharedFontFamilyList` so that people wouldn't get confused between this and the platform font lists.
Reporter | ||
Comment 23•7 years ago
|
||
mozreview-review-reply |
Comment on attachment 8913610 [details] Bug 1397626 - Part 1: Add a SharedFontList class. https://reviewboard.mozilla.org/r/184994/#review190824 > Probably `SharedFontList` (uppercase `L`) instead? It seems to me although there are variables called `fontlist`, types are consistently using `FontList`. > > Probably even better `SharedFontFamilyList` so that people wouldn't get confused between this and the platform font lists. If you don't want it to be that long, probably `SharedFamilyList` is also fine?
Reporter | ||
Comment 24•7 years ago
|
||
mozreview-review |
Comment on attachment 8914160 [details] Bug 1397626 - Part 2: Replace uses of FontFamilyListRefCnt with SharedFontList. https://reviewboard.mozilla.org/r/185488/#review190822 ::: layout/style/nsCSSParser.cpp:4042 (Diff revision 2) > REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily); > return false; > } > > // add family to rule > - const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue(); > + SharedFontlist* fontlist = fontlistValue.GetFontFamilyListValue(); It seems to me that we could keep the `const` here? ::: layout/style/nsCSSRules.h:193 (Diff revision 2) > - const mozilla::FontFamilyList& GetFamilyList() { return mFamilyList; } > - void SetFamilyList(const mozilla::FontFamilyList& aFamilyList); > + mozilla::SharedFontlist* GetFontlist() const { return mFontlist; } > + void SetFontlist(mozilla::SharedFontlist* aFontlist) { mFontlist = aFontlist; } With comment for part 1, it is probably better keep `family` in the name so that people wouldn't get confused. And it is probably better uppercase the `L`.
Attachment #8914160 -
Flags: review?(xidorn+moz) → review+
Reporter | ||
Comment 25•7 years ago
|
||
mozreview-review-reply |
Comment on attachment 8913610 [details] Bug 1397626 - Part 1: Add a SharedFontList class. https://reviewboard.mozilla.org/r/184994/#review190824 > If you don't want it to be that long, probably `SharedFamilyList` is also fine? Hmmm... It could be confused with `FontFamilyList` then... naming is hard :/ Probably fine just keep the current name...
Reporter | ||
Comment 26•7 years ago
|
||
mozreview-review |
Comment on attachment 8914161 [details] Bug 1397626 - Part 3: Use SharedFontList to store font-family specified and computed values. https://reviewboard.mozilla.org/r/185490/#review190830 ::: gfx/thebes/gfxFontFamilyList.h:170 (Diff revision 2) > - * A refcounted array of FontFamilyNames. > + * A refcounted array of FontFamilyNames. We use this to store the specified > + * value (in Servo) and the computed value (in both Gecko and Servo) of the > + * font-family property. Why is the additional comment removed in part 2 but added again in part 3? I guess you shouldn't add that part back. ::: gfx/thebes/gfxFontFamilyList.h:438 (Diff revision 2) > size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { > return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); > } > > -private: > - nsTArray<FontFamilyName> mFontlist; > +protected: > + RefPtr<SharedFontlist> mFontlist; Maybe consider wrapping this with `NotNull` so that people know that this would never be null. ::: layout/style/nsRuleNode.cpp:4379 (Diff revision 2) > uint8_t generic = kGenericFont_NONE; > // XXXldb What if we would have had a string if we hadn't been doing > // the optimization with a non-null aStartStruct? > const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); > if (eCSSUnit_FontFamilyList == familyValue->GetUnit()) { > - const SharedFontlist* fontlist = familyValue->GetFontFamilyListValue(); > + SharedFontlist* fontlist = familyValue->GetFontFamilyListValue(); Why do you need to remove `const` here?
Attachment #8914161 -
Flags: review?(xidorn+moz) → review+
Assignee | ||
Comment 27•7 years ago
|
||
mozreview-review-reply |
Comment on attachment 8914160 [details] Bug 1397626 - Part 2: Replace uses of FontFamilyListRefCnt with SharedFontList. https://reviewboard.mozilla.org/r/185488/#review190822 > It seems to me that we could keep the `const` here? I need to AddRef the object (in nsCSSFontFeatureValuesRule::SetFontlist), so it can't be const.
Assignee | ||
Comment 28•7 years ago
|
||
mozreview-review-reply |
Comment on attachment 8914161 [details] Bug 1397626 - Part 3: Use SharedFontList to store font-family specified and computed values. https://reviewboard.mozilla.org/r/185490/#review190830 > Why is the additional comment removed in part 2 but added again in part 3? I guess you shouldn't add that part back. Oh, I meant to remove the additional comment from part 1, and only add it in part 3.
Assignee | ||
Comment 29•7 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=e37aeaef8934770220181d700198e322cf7c4019
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Comment hidden (mozreview-request) |
Assignee | ||
Updated•7 years ago
|
Attachment #8913611 -
Attachment is obsolete: true
Assignee | ||
Comment 33•7 years ago
|
||
https://treeherder.mozilla.org/#/jobs?repo=try&revision=3426569b2f11df49c5425515593cb8fab8455c9f
Comment 34•7 years ago
|
||
Pushed by cmccormack@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/bb0291d4577f Part 1: Add a SharedFontList class. r=xidorn https://hg.mozilla.org/integration/autoland/rev/16c2166700f2 Part 2: Replace uses of FontFamilyListRefCnt with SharedFontList. r=xidorn https://hg.mozilla.org/integration/autoland/rev/d49cf4aed335 Part 3: Use SharedFontList to store font-family specified and computed values. r=xidorn
Comment 35•7 years ago
|
||
Pushed by ecoal95@gmail.com: https://hg.mozilla.org/integration/autoland/rev/00f3a339b197 Add missing explicit. r=me
Comment 36•7 years ago
|
||
bugherder |
https://hg.mozilla.org/mozilla-central/rev/bb0291d4577f https://hg.mozilla.org/mozilla-central/rev/16c2166700f2 https://hg.mozilla.org/mozilla-central/rev/d49cf4aed335 https://hg.mozilla.org/mozilla-central/rev/00f3a339b197
Status: ASSIGNED → RESOLVED
Closed: 7 years ago
status-firefox58:
--- → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla58
Reporter | ||
Comment 38•7 years ago
|
||
I don't think we should uplift this.
Assignee | ||
Comment 40•7 years ago
|
||
Agreed. (Assuming you meant the 57 train.)
You need to log in
before you can comment on or make changes to this bug.
Description
•