Looking at Vector's Binary Size

62 views
Skip to first unread message

Andrew Grieve

unread,
May 18, 2017, 1:44:21 PM5/18/17
to binary-size, chromium-dev, blink-dev
(cross-posting since binary-size@ is still new).

Let's look at the binary size cost of std::vector using //tools/binary_size/supersize.

>>> Print(size_info.symbols.WhereNameMatches(r'std::.*vector')) 
 
Showing 4,099 symbols (2,447 unique) with total pss: 417605 bytes
.text=407kb      .rodata=0 bytes    .data*=0 bytes    .bss=0 bytes    total=407kb
Number of unique paths: 1385
<snip> 

  • 407kb of code is due to vector
  • 4099 symbols that have std::vector in their name
  • Just under half of them have been identical-code-folded


Now, let's group them by their name without <>s:

>>> vector_syms = canned_queries.TemplatesByName().WhereNameMatches('vector') 
>>> Print(vector_syms) 
 
Showing 24 symbols (24 unique) with total pss: 417765 bytes
.text=407kb      .rodata=0 bytes    .data*=0 bytes    .bss=0 bytes    total=407kb
Number of unique paths: 1385

Index, Running Total, Section@Address, PSS
------------------------------------------------------------
0)     157674 (37.7%) *@Group      157674  std::__ndk1::vector::__push_back_slow_path (count=1782)
1)     219886 (52.6%) *@Group      62212   std::__ndk1::vector::insert (count=170)
2)     273534 (65.5%) *@Group      53648   std::__ndk1::vector::assign (count=229)
3)     321600 (77.0%) *@Group      48065   std::__ndk1::vector::__append (count=252)
4)     340658 (81.5%) *@Group      19057   std::__ndk1::vector::__emplace_back_slow_path (count=128)
5)     357022 (85.5%) *@Group      16364   std::__ndk1::__vector_base::~__vector_base (count=741)
6)     371468 (88.9%) *@Group      14446   std::__ndk1::vector::__swap_out_circular_buffer (count=266)
7)     383936 (91.9%) *@Group      12468   std::__ndk1::vector::vector (count=204)
8)     396388 (94.9%) *@Group      12452   std::__ndk1::vector::emplace (count=27)
9)     401998 (96.2%) *@Group      5610    std::__ndk1::vector::__construct_at_end (count=86)
10)    406456 (97.3%) *@Group      4457    std::__ndk1::vector::reserve (count=54)
11)    410156 (98.2%) *@Group      3700    std::__ndk1::vector::resize (count=42)
12)    412626 (98.8%) *@Group      2470    std::__ndk1::vector::erase (count=24)
13)    414746 (99.3%) *@Group      2120    std::__ndk1::vector::__move_range (count=17)
14)    416082 (99.6%) *@Group      1336    std::__ndk1::vector::deallocate (count=29)
15)    416834 (99.8%) *@Group      752     std::__ndk1::vector::__move_assign (count=11)
16)    417254 (99.9%) *@Group      419     std::__ndk1::vector::allocate (count=25)
17)    417440 (99.9%) *@Group      186     std::__ndk1::vector::push_back (count=1)
18)    417600 (100.0%) *@Group      160     google_breakpad::wasteful_vector::wasteful_vector (count=2)
19)    417680 (100.0%) *@Group      80      std::__ndk1::__vector_base_common::__throw_length_error const (count=1)
20)    417709 (100.0%) *@Group      29      std::__ndk1::vector::~vector (count=5)
21)    417729 (100.0%) *@Group      20      std::__ndk1::vector::clear (count=1)
22)    417747 (100.0%) *@Group      18      std::__ndk1::vector::max_size const (count=2)
23)    417765 (100.0%) *@Group      17      std::__ndk1::vector::swap (count=2)

  • This shows only symbols that have not been inlined.
  • The groupings here with low counts have had most of their usages inlined, so the total binary_size of vector is larger than what's shown.


Drilling into __push_back_slow_path

>>> Print(vector_syms[0], verbose=True)
 
Showing 1,782 symbols (787 unique) with total pss: 157674 bytes
.text=153kb      .rodata=0 bytes    .data*=0 bytes    .bss=0 bytes    total=153kb
Number of unique paths: 926

Index, Running Total, Section@Address, PSS
------------------------------------------------------------
0)        520 (0.3%)  t@0x14fe3e0  pss=520  padding=0  size_without_padding=520
             source_path=v8/src/wasm/module-decoder.cc  object_path=v8/v8_base/module-decoder.o
             flags={}  name=std::__ndk1::vector::__push_back_slow_path
                  full_name=std::__ndk1::vector<v8::internal::wasm::WasmIndirectFunctionTable, std::__ndk1::allocator<v8::internal::wasm::WasmIndirectFunctionTable> >::__push_back_slow_path<v8::internal::wasm::WasmIndirectFunctionTable>(v8::internal::wasm::WasmIndirectFunctionTable&&)
1)       1036 (0.7%)  t@0xb50a2c   pss=516  padding=0  size_without_padding=516
             source_path=third_party/skia/src/core/SkColorSpace_ICC.cpp         object_path=skia/skia/SkColorSpace_ICC.o
             flags={}  name=std::__ndk1::vector::__push_back_slow_path
                  full_name=std::__ndk1::vector<SkColorSpace_A2B::Element, std::__ndk1::allocator<SkColorSpace_A2B::Element> >::__push_back_slow_path<SkColorSpace_A2B::Element>(SkColorSpace_A2B::Element&&)
2)       1536 (1.0%)  t@0xe8d1b0   pss=500  padding=0  size_without_padding=500
             source_path=cc/trees/property_tree.cc      object_path=cc/cc/property_tree.o
             flags={}  name=std::__ndk1::vector::__push_back_slow_path
                  full_name=std::__ndk1::vector<cc::DrawTransformData, std::__ndk1::allocator<cc::DrawTransformData> >::__push_back_slow_path<cc::DrawTransformData>(cc::DrawTransformData&&)
<snip> 
  • __push_back_slow_path is likely never inlined (based on its high count).
  • The size of the symbols is proportional to the complexity of the template argument.


Let's see what the story is for vectors of pointers:

>>> Print(vector_syms[0].WhereFullNameMatches(r'vector<[^<]*\*').GroupedByName())
 
Showing 1 symbols (1 unique) with total pss: 1265 bytes
.text=1265 bytes    .rodata=0 bytes    .data*=0 bytes    .bss=0 bytes    total=1265 bytes
Number of unique paths: 307

Index, Running Total, Section@Address, PSS
------------------------------------------------------------
0)       1265 (100.0%) *@Group      1265    std::__ndk1::vector::__push_back_slow_path (count=442)

  • All other vector symbols for pointer types are fully inlined.
  • Showing it ungroup reveals
    • 442 symbols de-dupe down to just 10
    • 5 of the 10 are completely unique
    • The largest symbol is 220 bytes, and uses a custom allocator
    • The 10 unique symbols can likely be explained by there being multiple custom allocators, as well as different -O flags being used.
    • Interesting that we bother with custom allocators for vectors of pointers :P


And how about smart pointers?


>>> Print(vector_syms[0].WhereFullNameMatches(r'vector<[^<]*_ptr\b').GroupedByName())
 
Showing 1 symbols (1 unique) with total pss: 21619 bytes
.text=21.1kb     .rodata=0 bytes    .data*=0 bytes    .bss=0 bytes    total=21.1kb
Number of unique paths: 193

Index, Running Total, Section@Address, PSS
------------------------------------------------------------
0)      21619 (100.0%) *@Group      21619   std::__ndk1::vector::__push_back_slow_path (count=304)

  • Showing it ungroup reveals
    • 304 symbols de-dupe down to 105
  • It's not clear to me why there are so many unique symbols... Perhaps GCC is not as good at fully inlining these smart pointer types as it could be.


Bottom-line:
  • Using non-pointer types in vectors (especially non-PODs) has a real binary size cost, which affects all users, all the time, even when they are not running your code.
  • Use templated collection classes on non-pointer types only when there is real performance need for it. 

Jeremy Roman

unread,
May 18, 2017, 2:14:35 PM5/18/17
to Andrew Grieve, binary-size, chromium-dev, blink-dev
Making the (default, copy and move) constructors, destructor, and (copy and move) assignment operators out-of-line seems like it would address this problem, right (by giving those only one definition, so that they cannot be inlined in the std::vector template).
This seems like rather extreme advice (never put anything in a collection), and I'm not convinced it's realistic (we use non-POD all the time; even std::unique_ptr is non-POD).

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CABiQX1WHpgiwGwVHKk3BL0-ntVR8Kyy-FnKdusbu%3DR0%2BDy614A%40mail.gmail.com.

Peter Kasting

unread,
May 18, 2017, 2:15:05 PM5/18/17
to Andrew Grieve, binary-size, chromium-dev, blink-dev
On Thu, May 18, 2017 at 10:42 AM, Andrew Grieve <agr...@chromium.org> wrote:
  • Use templated collection classes on non-pointer types only when there is real performance need for it.
I'm concerned about the simplicity of this conclusion.

Can you give an example case where you would propose an alternate solution, and what the alternate solution is?

As a strawman, consider "replace vector<unique_ptr> with vector<T*>", where ownership is implied (so callers have to remember to delete things they take out).  While this might be a code size win, it's a clarity/safety/security loss.  It's not immediately clear that one justifies the other.  Similarly, "write a custom non-templated container to hold your smart pointers" (as we did with e.g. ScopedVector) has its own binary size costs, as well as other readability and maintenance costs.

You reported that across the whole binary we have 407kb of code relating to vector.  So the individual savings from changing any one thing are going to be significantly smaller.  This means we would also probably want to accept only small regressions in readability, safety, and any other tradeoffs we might be making, with whatever solutions we propose.

PK

Andrew Grieve

unread,
May 18, 2017, 2:47:34 PM5/18/17
to Jeremy Roman, Andrew Grieve, binary-size, chromium-dev, blink-dev
I don't think putting them out-of-line would affect whether they get inlined (although usual caveat here about me not being a C++ expert)
Yeah, this is too extreme I think. I didn't really mean to indicate that smart pointers are not worth their overhead (although I'd like to figure out if we can reduce it). 

I was hoping the larger point to be that the more sophisticated the class, the more binary size is required to put them into a collection.
E.g., the top three: WasmIndirectFunctionTable, SkColorSpace_A2B::Element, cc::DrawTransformData
Should have some serious thought about whether they really need to be allocated directly in the collection as opposed to on the head (even within a smart pointer... I really am assuming that overhead is fixable with compilers).

Andrew Grieve

unread,
May 18, 2017, 2:51:33 PM5/18/17
to Peter Kasting, Andrew Grieve, binary-size, chromium-dev, blink-dev
Yikes, no. I was not trying to suggest that using smart pointers in collections is bad.

Of the 157674 total bytes for __push_back_slow_path, the smart pointer ones make up 21619 (~14%).

Although it's actually not clear to me from this whether they would be just as large if we converted all bare types to uniqe_ptr<>... My hope is that it is. And also that we might figure out what it is about these smart pointers that make them different from their bare pointer equivalent.

dan...@chromium.org

unread,
May 18, 2017, 2:53:40 PM5/18/17
to Andrew Grieve, Jeremy Roman, binary-size, chromium-dev, blink-dev
To be clear, if these types were made to be POD types, the claim is that the binary size would decrease?
 
Should have some serious thought about whether they really need to be allocated directly in the collection as opposed to on the head (even within a smart pointer... I really am assuming that overhead is fixable with compilers).

 

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CABiQX1WHpgiwGwVHKk3BL0-ntVR8Kyy-FnKdusbu%3DR0%2BDy614A%40mail.gmail.com.


--
You received this message because you are subscribed to the Google Groups "blink-dev" group.

Andrew Grieve

unread,
May 18, 2017, 2:56:28 PM5/18/17
to dan...@chromium.org, Andrew Grieve, Jeremy Roman, binary-size, chromium-dev, blink-dev
From what I can tell, you need one specialization for each sized POD. E.g. a vector of struct { int, int} should de-dupe with a vector of struct {uint, uint}

Jeremy Roman

unread,
May 18, 2017, 2:58:26 PM5/18/17
to Andrew Grieve, binary-size, chromium-dev, blink-dev
Avoiding increases in binary size due to these being inlined is exactly why the [chromium-style] warning for making them out-of-line exists, IIUC.

See the cases which include the phrase "Complex class/struct" here:

LTO isn't presently incredibly aggressive, so generally inlining will only happen if the definition is available in the same translation unit. If only the declaration is available in the header and the definition is in the source file, this is usually sufficient to defeat inlining. (There are also explicit annotations, but we haven't used those much.)

Nico Weber

unread,
May 18, 2017, 3:02:17 PM5/18/17
to Andrew Grieve, binary-size, chromium-dev, blink-dev
On Thu, May 18, 2017 at 1:42 PM, Andrew Grieve <agr...@chromium.org> wrote:
Do you have an easy way to run the same thing on a linux build? libc++ (which we use on Linux) uses always_inline on many of its methods for ABI reasons, so I'd expect that this undercounts more on Android (where we use libc++) than on Linux (where we currently still use libstdc++).
 

--
You received this message because you are subscribed to the Google Groups "binary-size" group.
To unsubscribe from this group and stop receiving emails from it, send an email to binary-size+unsubscribe@chromium.org.
To post to this group, send email to binar...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/binary-size/CABiQX1WHpgiwGwVHKk3BL0-ntVR8Kyy-FnKdusbu%3DR0%2BDy614A%40mail.gmail.com.

Daniel Murphy

unread,
May 18, 2017, 4:10:39 PM5/18/17
to Nico Weber, Andrew Grieve, binary-size, chromium-dev, blink-dev
/me silently sheds a tear for memory locality as fragmentation wins again

To unsubscribe from this group and stop receiving emails from it, send an email to binary-size...@chromium.org.

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAMGbLiFq8OtRgT6kSeRRCDjP%2B71RaHjhBnFAJirx8oA%2Bsr2G0A%40mail.gmail.com.

Lucas Gadani

unread,
May 18, 2017, 4:11:40 PM5/18/17
to Andrew Grieve, binary-size, chromium-dev, blink-dev
On Thu, May 18, 2017 at 1:42 PM Andrew Grieve <agr...@chromium.org> wrote:

Bottom-line:
  • Using non-pointer types in vectors (especially non-PODs) has a real binary size cost, which affects all users, all the time, even when they are not running your code.

I think this recommendation is too simplistic. Using pointer types will incur the problems of reduced memory locality, extra allocator overhead, increased memory fragmentation.

At which point does the allocator overhead and fragmentation will outweight the benefits of using pointer types, only from a memory perspective? Is it worth doing it just for the sake of reducing the binary size by a few kb?

Daniel Murphy

unread,
May 18, 2017, 6:56:10 PM5/18/17
to Lucas Gadani, Andrew Grieve, binary-size, chromium-dev, blink-dev
+100000000
 

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.

David Turner

unread,
May 19, 2017, 6:11:09 AM5/19/17
to Daniel Murphy, Lucas Gadani, Andrew Grieve, binary-size, chromium-dev, blink-dev
FWIW, Android started without a decent STL and had to run on very memory constrained devices, so they designed their
own android::Vector<T> template type, that inherits from a non-templated base android::VectorImpl class.

To avoid code size explosion due to template instantiation, the generic VectorImpl methods call protected virtual methods for
non-trivial type-specific operations (copy/move/etc). This works really well to achieve the original goal. The tradeoffs are:
  • The size of and empty android::Vector<T> instance is about twice that of the corresponding std::vector<T>.
  • Construction, destruction and any mutating operations are slightly slower.
There is no loss of readability. However:
  • It's unsure adopting a similar strategy would save a lot of code size anyway. Even a 50% reduction would only save about 200 KiB, which seems tiny for something that requires changing all std::vector<> instances to something like base::Vector<>.
  • Most of Android has switched to std::vector<> once the STL support became decent, a long time ago, so even they don't consider it worth it anymore.
Due to this, I don't think there is much we can do here. Except maybe hack on the libc++ being used to link on Android to avoid some specific inlines?


To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+unsubscribe@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CA%2B4qT31rtj0xcO2aGVVgrehMy7yGjYCZX0tYjwoPJRK_VMekmg%40mail.gmail.com.

Andrew Grieve

unread,
May 19, 2017, 2:07:26 PM5/19/17
to Lucas Gadani, Andrew Grieve, binary-size, chromium-dev, blink-dev
I think it's clearly been established that this is certainly overly-simplistic, and the answer is always "it depends".

It's worth pointing out that locality matters for code as well though. The larger the code size, the more CPU is jumping around to get at instructions. If we could magically have only a single copy of all collection functions, I suspect there would be a real runtime speed boost from memory locality of the instructions. 


Andrew Grieve

unread,
May 19, 2017, 2:22:22 PM5/19/17
to Jeremy Roman, Andrew Grieve, binary-size, chromium-dev, blink-dev
Ah, okay, I interpreted "out-of-line" as meaning having the definition separate from the declaration, but still in the same .h file.
I'm not actually sure whether overly-aggressive inlining is a cause of bloat for us, but if it is, I think we could address it with NO_INLINE, and identical code folding would ensure we don't have the same function bodies more than once.

Daniel Cheng

unread,
May 19, 2017, 5:07:56 PM5/19/17
to Andrew Grieve, Lucas Gadani, binary-size, blink-dev, chromium-dev
While we got a few nice size wins (300 KB, 400KB-800KB) from uninlining CallbackBase and changing base::DictionaryValue to use StringPiece (to avoid implicit conversions to std::string all over the code), I never really saw anything to indicate it helped significantly with runtime speed. (It doesn't mean that they're not worth doing, and it could simply be that the things that changed weren't in sufficiently hot paths).

Also, I've noticed that binary size changes often have a reduced effect on Android. For example, removing the amount of implicit std::string objects generally had a negligible binary size impact on Android: see https://chromeperf.appspot.com/group_report?rev=414450. Is Android's standard library significantly different from other platforms? I thought it was libc++.

Daniel


--
You received this message because you are subscribed to the Google Groups "binary-size" group.
To unsubscribe from this group and stop receiving emails from it, send an email to binary-size...@chromium.org.
To post to this group, send email to binar...@chromium.org.

Nico Weber

unread,
May 19, 2017, 5:11:26 PM5/19/17
to Daniel Cheng, Andrew Grieve, Lucas Gadani, binary-size, blink-dev, chromium-dev
Android builds with -Os, linux with -O2. (We tried to unify that last week, but predictably it's not good for perf on Linux, see discussion on https://codereview.chromium.org/2883113002 . The list of benchmarks there that regressed is probably a list of metrics with optimization opportunity on Android...)

On Fri, May 19, 2017 at 5:06 PM, Daniel Cheng <dch...@chromium.org> wrote:
While we got a few nice size wins (300 KB, 400KB-800KB) from uninlining CallbackBase and changing base::DictionaryValue to use StringPiece (to avoid implicit conversions to std::string all over the code), I never really saw anything to indicate it helped significantly with runtime speed. (It doesn't mean that they're not worth doing, and it could simply be that the things that changed weren't in sufficiently hot paths).

Also, I've noticed that binary size changes often have a reduced effect on Android. For example, removing the amount of implicit std::string objects generally had a negligible binary size impact on Android: see https://chromeperf.appspot.com/group_report?rev=414450. Is Android's standard library significantly different from other platforms? I thought it was libc++.

Daniel
On Sat, May 20, 2017, 04:05 Andrew Grieve <agr...@chromium.org> wrote:
I think it's clearly been established that this is certainly overly-simplistic, and the answer is always "it depends".

It's worth pointing out that locality matters for code as well though. The larger the code size, the more CPU is jumping around to get at instructions. If we could magically have only a single copy of all collection functions, I suspect there would be a real runtime speed boost from memory locality of the instructions. 



On Thu, May 18, 2017 at 4:09 PM, Lucas Gadani <l...@chromium.org> wrote:
On Thu, May 18, 2017 at 1:42 PM Andrew Grieve <agr...@chromium.org> wrote:

Bottom-line:
  • Using non-pointer types in vectors (especially non-PODs) has a real binary size cost, which affects all users, all the time, even when they are not running your code.

I think this recommendation is too simplistic. Using pointer types will incur the problems of reduced memory locality, extra allocator overhead, increased memory fragmentation.

At which point does the allocator overhead and fragmentation will outweight the benefits of using pointer types, only from a memory perspective? Is it worth doing it just for the sake of reducing the binary size by a few kb?


--
You received this message because you are subscribed to the Google Groups "binary-size" group.
To unsubscribe from this group and stop receiving emails from it, send an email to binary-size+unsubscribe@chromium.org.

--
You received this message because you are subscribed to the Google Groups "binary-size" group.
To unsubscribe from this group and stop receiving emails from it, send an email to binary-size+unsubscribe@chromium.org.

To post to this group, send email to binar...@chromium.org.

Egor Pasko

unread,
May 22, 2017, 9:10:38 AM5/22/17
to Nico Weber, crou...@chromium.org, Daniel Cheng, Andrew Grieve, Lucas Gadani, binary-size, blink-dev, chromium-dev
On Fri, May 19, 2017 at 11:10 PM, Nico Weber <tha...@chromium.org> wrote:
Android builds with -Os, linux with -O2.

We build parts with -O2 on Android  (in base, skia, cc, brotli, webp, gfx).

(We tried to unify that last week, but predictably it's not good for perf on Linux, see discussion on https://codereview.chromium.org/2883113002 . The list of benchmarks there that regressed is probably a list of metrics with optimization opportunity on Android...)

I see only media_perftests linked to the bug. I think crouleau@ is the owner. Perhaps he can say whether android paths are likely sensitive to -O2. As usual we have to evaluate the binary-size/performance tradeoff if we wanted such a change on Android.
 

bratell

unread,
May 22, 2017, 10:54:33 AM5/22/17
to binary-size, chromi...@chromium.org, blin...@chromium.org
Most of the collection machine code "bloat" is because of inline construction, destruction, copy and move. It's pretty trivial to outline them with "=default" if you know they are big but that isn't always obvious. For instance a class with a couple of std::string member will have large operators without there being a single code line.

My way of identifying those classes was to build with and without -fno-inline and then compare sizes. If a collection function instance shrank a lot with -fno-inline, then it was worth tuning the class used in that instance. Still, unlikely to save more than 2-10 KB per patch so it's not very thankful work.

A note for those talking about memory locality, remember instruction cache too. Some of C++/STL's eager inlining do bad things with those.

/Daniel

Andrew Grieve

unread,
Jun 26, 2017, 10:44:27 AM6/26/17
to bratell, binary-size, chromium-dev, blink-dev
Daniel - sorry for the ignorance, but why does adding =default prevent inlining?

--
You received this message because you are subscribed to the Google Groups "binary-size" group.
To unsubscribe from this group and stop receiving emails from it, send an email to binary-size+unsubscribe@chromium.org.
To post to this group, send email to binar...@chromium.org.

Jeremy Roman

unread,
Jun 26, 2017, 10:52:21 AM6/26/17
to Andrew Grieve, bratell, binary-size, chromium-dev, blink-dev
I believe he's referring to making them out-of-line (and once implementing them out-of-line, "= default;" can be used to get an implementation equivalent to the one the compiler would have generated inline).

e.g.

// my_class.h
class MyClass {
 public:
  MyClass(MyClass&&);
  // etc
};

// my_class.cc
MyClass::MyClass(MyClass&&) = default;

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CABiQX1W3zikDFzd_r_GxR-R72h4S6XweWbHPcdeW3oR2emb4cw%40mail.gmail.com.

Nico Weber

unread,
Jun 26, 2017, 10:53:56 AM6/26/17
to Jeremy Roman, Andrew Grieve, bratell, binary-size, chromium-dev, blink-dev
For a template, wouldn't you have to explicitly specify all used template parameter types to out of line members?

Andrew Grieve

unread,
Jun 26, 2017, 11:00:53 AM6/26/17
to Nico Weber, Jeremy Roman, Andrew Grieve, bratell, binary-size, chromium-dev, blink-dev
Ah, that makes sense then (at least for non-templated types).
Reply all
Reply to author
Forward
0 new messages