Skip to content

Instantly share code, notes, and snippets.

@mraleph
Last active June 16, 2020 13:45
Embed
What would you like to do?

First create a hello.dart file with the following content.

void main(List<String> args) {
  print('Hello, World!');
}

Next we precompile this file using PRODUCT AOT compiler (equivalent of running flutter build aot --release).

$ env DART_CONFIGURATION=ProductX64 pkg/vm/tool/precompiler2 -v -Ddart.vm.product=true --write-v8-snapshot-profile-to=/tmp/profile.json /tmp/hello.dart /tmp/snapshot.elf

This should produce /tmp/profile.json V8 snapshot profile.

We can then analyse it in different ways, for example we can ask to print which packages contributed most bytes to the output:

$ pkg/vm/bin/snapshot_analysis.dart summary -b package /tmp/profile.json
+------------------------+--------------+---------+
| Package                | Size (Bytes) | Percent |
+------------------------+--------------+---------+
| dart:core              |       280419 |  36.68% |
| dart:typed_data        |       139142 |  18.20% |
| @unknown               |        53289 |   6.97% |
| dart:collection        |        51978 |   6.80% |
| dart:async             |        45975 |   6.01% |
| dart:io                |        41018 |   5.37% |
| dart:convert           |        36971 |   4.84% |
| dart:isolate           |        32945 |   4.31% |
| @stubs                 |        21979 |   2.87% |
| @shared                |        21039 |   2.75% |
| dart:_internal         |        17491 |   2.29% |
| dart:_builtin          |         8081 |   1.06% |
| dart:ffi               |         3493 |   0.46% |
| dart:math              |         3215 |   0.42% |
| dart:developer         |         2036 |   0.27% |
| dart:vmservice_io      |         1388 |   0.18% |
| dart:wasm              |         1257 |   0.16% |
| dart:cli               |         1090 |   0.14% |
| dart:_http             |          606 |   0.08% |
| file:///tmp/hello.dart |          536 |   0.07% |
| dart:mirrors           |          399 |   0.05% |
| dart:nativewrappers    |          117 |   0.02% |
| dart:_vmservice        |           32 |   0.00% |
+------------------------+--------------+---------+
Total: 764496 bytes

Breakdown by object type:
+--------------------------+--------------+---------+
| Type                     | Size (Bytes) | Percent |
+--------------------------+--------------+---------+
| (RO) Instructions        |       401744 |  52.55% |
| (RO) _OneByteString      |       119392 |  15.62% |
| (RO) CodeSourceMap       |        61728 |   8.07% |
| (RO) CompressedStackMaps |        38720 |   5.06% |
| Function                 |        30538 |   3.99% |
| Array                    |        27900 |   3.65% |
| Code                     |        24988 |   3.27% |
| Class                    |        17024 |   2.23% |
| Type                     |         8461 |   1.11% |
| int                      |         6717 |   0.88% |
| ObjectPool               |         5393 |   0.71% |
| TypeArguments            |         4120 |   0.54% |
| DispatchTable            |         3200 |   0.42% |
| OneByteString            |         2889 |   0.38% |
| TypeParameter            |         2337 |   0.31% |
| Field                    |         1437 |   0.19% |
| Script                   |         1148 |   0.15% |
| ArtificialRoot           |         1116 |   0.15% |
| CodeSourceMap            |         1093 |   0.14% |
| PatchClass               |          828 |   0.11% |
| CompressedStackMaps      |          784 |   0.10% |
| (RO) PcDescriptors       |          672 |   0.09% |
| TypedData                |          515 |   0.07% |
| Library                  |          400 |   0.05% |
| ClosureData              |          372 |   0.05% |
| SignatureData            |          242 |   0.03% |
| ExceptionHandlers        |          180 |   0.02% |
| GrowableObjectArray      |          143 |   0.02% |
| Closure                  |          136 |   0.02% |
| Instance                 |           61 |   0.01% |
| UnlinkedCall             |           56 |   0.01% |
| (RO) InstructionsSection |           48 |   0.01% |
| SubtypeTestCache         |           34 |   0.00% |
| Image                    |           32 |   0.00% |
| MegamorphicCache         |           24 |   0.00% |
| PcDescriptors            |           18 |   0.00% |
| TypeRef                  |            4 |   0.00% |
| double                   |            2 |   0.00% |
| ArgumentsDescriptor      |            0 |   0.00% |
| Bytecode                 |            0 |   0.00% |
| ContextScope             |            0 |   0.00% |
| bool                     |            0 |   0.00% |
| LocalVarDescriptors      |            0 |   0.00% |
| Null                     |            0 |   0.00% |
+--------------------------+--------------+---------+
Total: 764496 bytes

--------------------------------------------------------------------------------
IMPORTANT: Dart AOT snapshot is a serialized representation of Dart VM heap.
Outside of few specific cases (e.g. an object representing a library clearly
originates from the library it represents) there is no well defined relationship
between snapshot bytes and a specific method/class/library to which these
bytes can be attributed with certainty. This snapshot analysis tool tries
to attribute bytes to specific program structure elements based on their
reachability from objects with well defined origin - meaning that this analysis
has some margin of error and imprecision.

- @other bucket denotes bytes attributed to entities outside of the current
granularity. For example, when breaking down the size by method name there
might be bytes which exist outside of any specific symbol - in which case
they will be attributed to @other.
- @stubs bucket accumulates bytes attributed to stubs (pieces of machine code
produced by the VM for internal purposes).
- @shared bucket accumulates bytes shared between otherwise unrelated program
entities
- @unknown bucket accumulates bytes which are not reachable from any program
structure nodes (usually VM internal objects).
--------------------------------------------------------------------------------

Note how this output also includes information about which types of objects are there in the snapshot.

We can look at the size of a specific item by using --where flag.

$ pkg/vm/bin/snapshot_analysis.dart summary -b class -w dart:core /tmp/profile.json
+-----------+------------------------+--------------+---------+----------+
| Library   | Class                  | Size (Bytes) | Percent | Of total |
+-----------+------------------------+--------------+---------+----------+
| dart:core | _Uri                   |        43563 |  15.53% |    5.70% |
| dart:core | _StringBase            |        28831 |  10.28% |    3.77% |
| dart:core | ::                     |        27559 |   9.83% |    3.60% |
| @other    |                        |        25467 |   9.08% |    3.33% |
| dart:core | Uri                    |        14936 |   5.33% |    1.95% |
| dart:core | int                    |        12276 |   4.38% |    1.61% |
| dart:core | NoSuchMethodError      |        12222 |   4.36% |    1.60% |
| dart:core | _IntegerImplementation |         8953 |   3.19% |    1.17% |
| dart:core | _OneByteString         |         7718 |   2.75% |    1.01% |
| dart:core | _GrowableList          |         6976 |   2.49% |    0.91% |
| dart:core | _InvocationMirror      |         6772 |   2.41% |    0.89% |
| dart:core | _Smi                   |         5536 |   1.97% |    0.72% |
| dart:core | StringBuffer           |         5529 |   1.97% |    0.72% |
| dart:core | _SimpleUri             |         5098 |   1.82% |    0.67% |
| dart:core | _Double                |         4850 |   1.73% |    0.63% |
| dart:core | UriData                |         4631 |   1.65% |    0.61% |
| dart:core | _List                  |         4303 |   1.53% |    0.56% |
| dart:core | Object                 |         3641 |   1.30% |    0.48% |
| dart:core | FormatException        |         3567 |   1.27% |    0.47% |
| dart:core | List                   |         3394 |   1.21% |    0.44% |
| dart:core | _Closure               |         3117 |   1.11% |    0.41% |
| dart:core | Duration               |         3028 |   1.08% |    0.40% |
| dart:core | _ImmutableMap          |         2878 |   1.03% |    0.38% |
| dart:core | _RegExp                |         2675 |   0.95% |    0.35% |
| dart:core | RangeError             |         2633 |   0.94% |    0.34% |
| dart:core | ArgumentError          |         2492 |   0.89% |    0.33% |
| dart:core | _AssertionError        |         2352 |   0.84% |    0.31% |
| dart:core | Iterable               |         2209 |   0.79% |    0.29% |
| dart:core | _ImmutableList         |         1697 |   0.61% |    0.22% |
| dart:core | _RegExpMatch           |         1675 |   0.60% |    0.22% |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         44 more rows accounting for 19841 (2.60% of total) bytes         
               on average that is 451 (0.16%) bytes per row               
+-----------+------------------------+--------------+---------+----------+
In visible rows: 260578 (34.08% of total)
Total: 764496 bytes

Breakdown by object type:
+--------------------------+--------------+---------+----------+
| Type                     | Size (Bytes) | Percent | Of total |
+--------------------------+--------------+---------+----------+
| (RO) Instructions        |       172712 |  61.59% |   22.59% |
| (RO) _OneByteString      |        35536 |  12.67% |    4.65% |
| (RO) CodeSourceMap       |        24880 |   8.87% |    3.25% |
| (RO) CompressedStackMaps |        13888 |   4.95% |    1.82% |
| Function                 |        10051 |   3.58% |    1.31% |
| Code                     |         7947 |   2.83% |    1.04% |
| Array                    |         5399 |   1.93% |    0.71% |
| Class                    |         3828 |   1.37% |    0.50% |
| int                      |         1657 |   0.59% |    0.22% |
| Type                     |         1505 |   0.54% |    0.20% |
| OneByteString            |          877 |   0.31% |    0.11% |
| TypeArguments            |          524 |   0.19% |    0.07% |
| CodeSourceMap            |          373 |   0.13% |    0.05% |
| CompressedStackMaps      |          284 |   0.10% |    0.04% |
| Field                    |          281 |   0.10% |    0.04% |
| TypeParameter            |          279 |   0.10% |    0.04% |
| PatchClass               |          192 |   0.07% |    0.03% |
| ClosureData              |           87 |   0.03% |    0.01% |
| (RO) PcDescriptors       |           32 |   0.01% |    0.00% |
| SignatureData            |           19 |   0.01% |    0.00% |
| Closure                  |           16 |   0.01% |    0.00% |
| ExceptionHandlers        |           10 |   0.00% |    0.00% |
| TypedData                |            8 |   0.00% |    0.00% |
| UnlinkedCall             |            8 |   0.00% |    0.00% |
| MegamorphicCache         |            8 |   0.00% |    0.00% |
| Instance                 |            6 |   0.00% |    0.00% |
| SubtypeTestCache         |            5 |   0.00% |    0.00% |
| TypeRef                  |            4 |   0.00% |    0.00% |
| double                   |            2 |   0.00% |    0.00% |
| PcDescriptors            |            1 |   0.00% |    0.00% |
| ArgumentsDescriptor      |            0 |   0.00% |    0.00% |
+--------------------------+--------------+---------+----------+
In visible rows: 280419 (36.68% of total)
Total: 764496 bytes
$ pkg/vm/bin/snapshot_analysis.dart summary -b method -w dart:core::_Uri /tmp/profile.json
+-----------+------------------------------------------+--------------+---------+----------+
| Library   | Symbol                                   | Size (Bytes) | Percent | Of total |
+-----------+------------------------------------------+--------------+---------+----------+
| dart:core | _Uri._normalizeRegName                   |         3765 |   8.64% |    0.49% |
| @other    |                                          |         3732 |   8.57% |    0.49% |
| dart:core | _Uri._normalizeZoneID                    |         3000 |   6.89% |    0.39% |
| dart:core | _Uri._makeWindowsFileUrl                 |         2510 |   5.76% |    0.33% |
| dart:core | _Uri.resolveUri                          |         2406 |   5.52% |    0.31% |
| dart:core | _Uri._normalize                          |         2219 |   5.09% |    0.29% |
| dart:core | _Uri._makeHost                           |         2097 |   4.81% |    0.27% |
| dart:core | _Uri._uriEncode                          |         1917 |   4.40% |    0.25% |
| dart:core | _Uri._normalizeRelativePath              |         1780 |   4.09% |    0.23% |
| dart:core | _Uri._mergePaths                         |         1550 |   3.56% |    0.20% |
| dart:core | _Uri.new _Uri.notSimple                  |         1308 |   3.00% |    0.17% |
| dart:core | _Uri.==                                  |         1226 |   2.81% |    0.16% |
| dart:core | _Uri._escapeChar                         |         1203 |   2.76% |    0.16% |
| dart:core | _Uri.new _Uri.                           |         1179 |   2.71% |    0.15% |
| dart:core | _Uri._removeDotSegments                  |         1147 |   2.63% |    0.15% |
| dart:core | _Uri._makeScheme                         |         1095 |   2.51% |    0.14% |
| dart:core | _Uri._checkWindowsPathReservedCharacters |         1024 |   2.35% |    0.13% |
| dart:core | _Uri._normalizeEscape                    |         1018 |   2.34% |    0.13% |
| dart:core | _Uri._escapeScheme                       |          979 |   2.25% |    0.13% |
| dart:core | _Uri._makePath                           |          752 |   1.73% |    0.10% |
| dart:core | _Uri._initializeText                     |          558 |   1.28% |    0.07% |
| dart:core | _Uri._checkWindowsDriveLetter            |          455 |   1.04% |    0.06% |
| dart:core | _Uri._makeFileUri                        |          430 |   0.99% |    0.06% |
| dart:core | _Uri._checkZoneID                        |          382 |   0.88% |    0.05% |
| dart:core | _Uri._canonicalizeScheme                 |          367 |   0.84% |    0.05% |
| dart:core | _Uri._makeQuery                          |          338 |   0.78% |    0.04% |
| dart:core | _Uri._normalizeOrSubstring               |          338 |   0.78% |    0.04% |
| dart:core | _Uri._makeFragment                       |          334 |   0.77% |    0.04% |
| dart:core | _Uri._writeAuthority                     |          330 |   0.76% |    0.04% |
| dart:core | _Uri._makeUserInfo                       |          326 |   0.75% |    0.04% |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                  27 more rows accounting for 3798 (0.50% of total) bytes                   
                        on average that is 141 (0.32%) bytes per row                        
+-----------+------------------------------------------+--------------+---------+----------+
In visible rows: 39765 (5.20% of total)
Total: 764496 bytes

Breakdown by object type:
+--------------------------+--------------+---------+----------+
| Type                     | Size (Bytes) | Percent | Of total |
+--------------------------+--------------+---------+----------+
| (RO) Instructions        |        30152 |  69.21% |    3.94% |
| (RO) CodeSourceMap       |         4576 |  10.50% |    0.60% |
| (RO) _OneByteString      |         3712 |   8.52% |    0.49% |
| (RO) CompressedStackMaps |         2640 |   6.06% |    0.35% |
| Function                 |         1129 |   2.59% |    0.15% |
| Code                     |          906 |   2.08% |    0.12% |
| Array                    |          206 |   0.47% |    0.03% |
| OneByteString            |           87 |   0.20% |    0.01% |
| Field                    |           60 |   0.14% |    0.01% |
| CodeSourceMap            |           47 |   0.11% |    0.01% |
| CompressedStackMaps      |           43 |   0.10% |    0.01% |
| ClosureData              |            5 |   0.01% |    0.00% |
+--------------------------+--------------+---------+----------+
In visible rows: 43563 (5.70% of total)
Total: 764496 bytes

We can also edit our hello.dart to import more from dart:io and see how this affects the size of the snapshot

import 'dart:io';

void main(List<String> args) {
  print(File(Platform.script.toFilePath()).readAsStringSync());
}
env DART_CONFIGURATION=ProductX64 pkg/vm/tool/precompiler2 -v -Ddart.vm.product=true --write-v8-snapshot-profile-to=/tmp/profile2.json /tmp/hello.dart /tmp/snapshot.elf
pkg/vm/bin/snapshot_analysis.dart compare -b package /tmp/profile.json /tmp/profile2.json 
+------------------------+--------------+---------+
| Package                | Diff (Bytes) | Percent |
+------------------------+--------------+---------+
| dart:io                |        24555 |  47.63% |
| dart:core              |        22775 |  44.18% |
| dart:async             |         1526 |   2.96% |
| @unknown               |         1101 |   2.14% |
| dart:_internal         |          745 |   1.45% |
| @shared                |          436 |   0.85% |
| dart:typed_data        |          286 |   0.55% |
| file:///tmp/hello.dart |          263 |   0.51% |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| dart:wasm              |          -33 |  -0.06% |
| dart:_builtin          |          -33 |  -0.06% |
| dart:isolate           |          -69 |  -0.13% |
+------------------------+--------------+---------+
Total: 51552 bytes
Comparing /tmp/profile.json (old) to /tmp/profile2.json (new)
Old   : 764496 bytes.
New   : 816048 bytes.
Change: +51552 bytes.
$ pkg/vm/bin/snapshot_analysis.dart compare -b class /tmp/profile.json /tmp/profile2.json
+------------------------+--------------------------+--------------+---------+
| Library                | Class                    | Diff (Bytes) | Percent |
+------------------------+--------------------------+--------------+---------+
| dart:core              | _SimpleUri               |        11519 |  22.34% |
| dart:core              | _Uri                     |         6563 |  12.73% |
| dart:io                | _RandomAccessFile        |         5337 |  10.35% |
| @other                 |                          |         4009 |   7.78% |
| dart:io                | _FileResourceInfo        |         3809 |   7.39% |
| dart:core              | Stopwatch                |         2736 |   5.31% |
| dart:io                | _File                    |         2432 |   4.72% |
| dart:io                | _CopyingBytesBuilder     |         2415 |   4.68% |
| dart:io                | _IOResourceInfo          |         2251 |   4.37% |
| dart:io                | _ReadWriteResourceInfo   |         1666 |   3.23% |
| dart:io                | _BytesBuilder            |         1617 |   3.14% |
| dart:io                | _Platform                |         1439 |   2.79% |
| dart:io                | _RandomAccessFileOpsImpl |         1131 |   2.19% |
| dart:async             | _Future                  |         1078 |   2.09% |
| dart:core              | Uri                      |          804 |   1.56% |
| dart:io                | BytesBuilder             |          507 |   0.98% |
| dart:async             | Future                   |          425 |   0.82% |
| dart:core              | DateTime                 |          408 |   0.79% |
| dart:_internal         | ::                       |          392 |   0.76% |
| dart:_internal         | VMLibraryHooks           |          373 |   0.72% |
| dart:typed_data        | Uint8List                |          298 |   0.58% |
| file:///tmp/hello.dart | ::                       |          263 |   0.51% |
| dart:core              | List                     |          226 |   0.44% |
| dart:io                | _RandomAccessFileOps     |          165 |   0.32% |
| dart:core              | _GrowableList            |          132 |   0.26% |
| dart:io                | Platform                 |          102 |   0.20% |
| dart:core              | ::                       |           27 |   0.05% |
| dart:async             | ::                       |           17 |   0.03% |
| dart:io                | _ExternalBuffer          |            2 |   0.00% |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           10 more rows accounting for -178 (-0.35% of total) bytes           
                on average that is -18 (-0.03%) bytes per row                 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| dart:isolate           | _RawReceivePortImpl      |          -33 |  -0.06% |
| dart:core              | _LateInitializationError |          -33 |  -0.06% |
| dart:_builtin          | ::                       |          -33 |  -0.06% |
| dart:core              | UriData                  |          -33 |  -0.06% |
| dart:io                | InternetAddressType      |          -33 |  -0.06% |
| dart:isolate           | _Timer                   |          -36 |  -0.07% |
| dart:io                | _SocketProfile           |          -41 |  -0.08% |
| dart:io                | _IOService               |          -49 |  -0.10% |
| dart:core              | _IntegerImplementation   |          -50 |  -0.10% |
| dart:core              | _StringMatch             |          -72 |  -0.14% |
+------------------------+--------------------------+--------------+---------+
In visible rows: 51730 (100.35% of total)
Total: 51552 bytes
Comparing /tmp/profile.json (old) to /tmp/profile2.json (new)
Old   : 764496 bytes.
New   : 816048 bytes.
Change: +51552 bytes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment