dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.23k stars 1.57k forks source link

Customers want guidance and docs for tree-shaking #33920

Open matanlurey opened 6 years ago

matanlurey commented 6 years ago

Despite tree-shaking being a prevalent feature/reason to use the language:

Dead code elimination in dynamic languages is a much harder problem than in static languages. The idea of a "treeshaker" originated in LISP2 in the 1990s. The idea is that all possible execution flows of a program can be represented as a tree of function calls, so that functions that are never called can be eliminated.

The algorithm was applied to JavaScript in Google Closure Tools and then to Dart in the dart2js compiler also written by Google, presented by Bob Nystrom in 2012 and described by the book "Dart in Action" by author Chris Buckett in 2013:

... it's not documented:

In fact the only references I can find anywhere to this feature is on the Dart2JS page:

Don’t worry about the size of your app’s included libraries. The dart2js tool performs tree shaking to omit unused classes, functions, methods, and so on. Just import the libraries you need, and let dart2js get rid of what you don’t need.

This has led customers to wild assumptions around what is and what is not tree-shakeable, and without any clear guidance to patterns that allow or disallow tree-shaking. For example internally, many large applications chose to store configurable metadata in a hash-map:

final _config = <String, Stuff>{};

class Stuff {
  void doStuff() {}
}
class FooStuff extends Stuff {}
class BarStuff extends Stuff {}

void main() {
  _config['foo'] = new FooStuff();
  _config['bar'] = new BarStuff();

  // ... elsewhere in the app ...
 config['foo'].doStuff();
}

... _config['bar'] isn't tree-shaken, nor is class BarStuff. Users did not believe this until I showed them (this issue is not about what should be tree-shaken or why or why not). I believe that tree-shaking is also a selling point for Dart on Flutter, but there is no documentation for Dart AOT either.

I'd personally like to see:

@hterkelsen at one point wrote this: https://hterkelsen.github.io/dart2js_playground/

It could be a nice start.

matanlurey commented 6 years ago

Another starting point might be looking at what the Closure Compiler documents:

kevmoo commented 6 years ago

CC @kwalrath

matanlurey commented 6 years ago

To be clear, I don't know if its possible for @kwalrath to write anything (other than a brief introduction) without the implementation teams. I'm happy to open meta issues for Dart2JS team and Dart AOT team too.

lexaknyazev commented 6 years ago

Possible option to get some insights about dart2js tree-shaking is to use Dump-Info Visualizer:

  1. Run dart2js on some sources with --dump-info.
  2. Drop its result into https://dart-lang.github.io/dump-info-visualizer/
matanlurey commented 6 years ago

That is not a realistic documentation medium for the majority of folks.

matanlurey commented 6 years ago

An external S/O related to tree-shaking for AoT (Flutter): https://stackoverflow.com/questions/52822353/how-to-test-debug-tree-shaking-in-flutter

/cc @a-siva @mraleph

rrousselGit commented 6 years ago

I made that S/O question (+ partial answer)

I think it would be important to be able to test tree shaking. With flutter it is relatively easy to include Cupertino widgets on an Android app by inadvertance.

mraleph commented 6 years ago

We are looking to build tools that would allow you to inspect what is included into the resulting snapshot. The bug to track is https://github.com/dart-lang/sdk/issues/34632

There is already a rudimentary tooling for that (--print-instructions-sizes-to), but nothing that is nicely packaged for consumption.

/cc @sjindel-google

matanlurey commented 6 years ago

@mraleph I think having tools is awesome, but this request is around docs. Most users will not want to write N-versions of their code, run analysis tools, and figure out when tree-shaking is applied or not. There needs to be some common sense rules/docs for most users.

mraleph commented 6 years ago

@matanlurey it is very hard to write a good doc because results of the tree shaking highly depend on optimizations and describing those would be hard.

matanlurey commented 6 years ago

@mraleph: How hard? I understand that it's practically speaking not possible to write a specification for dead code elimination - but I think a simple FAQ would go a long way. For example, knowing that certain constant booleans will tree-shake, that complex data structures (like lists or maps) will not, etc.

matanlurey commented 6 years ago

I just think its silly to say "we provide awesome dead code elimination" and then offer no samples or guidance around when it applies.

JeromePuttemans commented 5 years ago

Hello,

Why not begin with some basic examples ?

Here is a little contribution with some experimental tests trying to understand tree-shaking.

Do you have other interesting tips/explanations to force tree-shaking ?

rockingdice commented 5 years ago

I was lead to this issue from my S/O question: https://stackoverflow.com/questions/56306140/will-the-dart-codes-for-ios-be-removed-when-compiling-for-android I'm still expecting an answer but seems it's not easy to answer just Yes or No. My understanding from all the posts above would be it depends on the logic I write for different platforms. Is there any solution to test the tree-shaking logic by now?

I think even the optimization is complicated, it's still logical and there must be some principles or directions to follow, like avoid to do something or manage to do something for tree-shaking codes.

But eventually, we have to make sure it's working as expected.

rrousselGit commented 5 years ago

There's currently no way to have an if/else depend on the platform target and still be code tree-shaked.

The only solution for tree shaking to eliminate iOS code on Android is to use different main.dart (using flutter build -t lib/my_main.dart) combined with a factory+singleton pattern. But that's not something trivial.

rockingdice commented 5 years ago

There's currently no way to have an if/else depend on the platform target and still be code tree-shaked.

The only solution for tree shaking to eliminate iOS code on Android is to use different main.dart (using flutter build -t lib/my_main.dart) combined with a factory+singleton pattern. But that's not something trivial.

Thank you for your answer. I think it's convincing and I'll think another way to work around it. Like using channels to make native libs for different platforms. Do you have a way to test this case? I tried your method from your question using Observation but don't know how to check within the code level.

aspantel commented 5 years ago

We just noticed that dart2js removes some getters that are used in our code and our Angular app gets MethodNotFound/not a function in runtime :( We have to run with -O0 now to make the app work. Any suggestions on how to understand the reason for dropping the live code? Thanks

aspantel commented 5 years ago

I have a feeling v2.4.0 broke this for us. Now, I am thinking dart2js has a bug in tree shaking, removing live code that the previous verion was not removing. Where should I file a bug on this. I can't imagine how we'd provide a reproducible testcase.

mraleph commented 5 years ago

@yoxel if your code is failing because dart2js is dropping some live methods then this would be a bug. please file a separate issue for it.

corymalcolmtaylor commented 4 years ago

I am trying to squish a bug that exists in release mode but not it debug mode and was wondering if tree shaking might be part of the problem. Is there a way to turn off tree shaking when using the 'Flutter build web' command? I can supply the code sample if you wish. See here: https://github.com/flutter/flutter/issues/54026

fzyzcjy commented 4 years ago

Hi 2 years has passed - any updates? Thanks

mraleph commented 4 years ago

@fzyzcjy are you asking from Web perspective (the issue has been labeled as mostly targeting Web) or Native perspective?

For native we now have package:vm_snapshot_analysis for analysing resulting binary size which gives you some insight into what got pulled into the resulting AOT snapshot. This information is also being exposed in Flutter Dev Tools.

fzyzcjy commented 4 years ago

@mraleph Wow I did not know that before! I will have a look at it! (I am asking the native)

mraleph commented 4 years ago

@fzyzcjy fwiw I can't find any public documentation for the Code Size analysis views in Flutter Dev Tools (maybe because it is still being developed - or maybe I am looking in the wrong place. @kenzieschmoll might know where to look).

fzyzcjy commented 4 years ago

@mraleph I did a search and also did not find it. But the package looks promising!

kenzieschmoll commented 4 years ago

Docs for the DevTools code size tool are here: https://flutter.dev/docs/development/tools/devtools/code-size

lohanbodevan commented 4 years ago

Hey @kenzieschmoll, thanks for sharing the docs. Is it already available on Flutter 1.20.3? I'm trying to use it but I'm getting Could not find an option named "analyze-size". I couldn't find anything about it on flutter build apk --help. Am I doing something wrong?

kenzieschmoll commented 4 years ago

You need to be on 1.22.0-9.0.pre or greater, which should currently be on the dev branch

fzyzcjy commented 4 years ago

A related question: I write some native code (c++) and use dart:ffi to bind to Flutter. However, with only several functions it is 1.8MB size. Thus, I wonder some tools which is similar to vm_snapshot_analysis and can help me dig out which class/method/... takes space?

Thanks very much!

mraleph commented 4 years ago

@fzyzcjy you can use https://github.com/google/bloaty for C++ code, alternatively there is also a tool in Dart SDK sources (https://github.com/dart-lang/sdk/blob/master/runtime/third_party/binary_size/src/run_binary_size_analysis.py) which you could try to use.

fzyzcjy commented 4 years ago

@mraleph Thanks very much! However, I have tried using bloaty, but it says most of the size is for TEXT region though I give it debug symbols... The issue: https://github.com/google/bloaty/issues/213

xuanswe commented 3 years ago

Beside of testing tree-shaking manually with devtools, is there an official doc to confirm how dart and flutter optimize code in general with tree shaking on android/iOS or not?

In this flutter doc, it doesn't say that tree shaking will be used for android/iOS.

Flutter will only do tree shaking when build for web: "The build is minified and tree shaking has been performed."

For non-web builds: "Compilation is optimized for fast startup, fast execution, and small package sizes." => does it mean with or without tree shaking? How does it actually optimize for android/iOS and other platform? Can some one confirm with deeper information on this?

adnanjpg commented 3 years ago

It's been 3 years, is there any update? thanks.

cedvdb commented 3 years ago

It would be nice to have some information and rough guidelines indeed. Why can't maps and list be tree shaken ?

dungnv2602 commented 3 years ago

In Flutter Web release mode, there is no doubt that dart2js supports tree-shaking in as it was mentioned here & here.

According to this post, I can 'safely' say that Flutter Engine supports tree-shaking in code & assets in Profile or Release mode.

It would be great if anyone from Flutter Team can verify this.

venkatd commented 2 years ago

What would be involved for us to get a command line tool that, similar to a code coverage tool, would tell us which lines of code are and aren't included in a final build?

Even if we don't have any specific guidance, I have no idea whether some piece of code made it into the final build.

harryterkelsen commented 2 years ago

There is this (possibly outdated) tool which shows all of the code which made it into the compiled app: https://github.com/dart-lang/dump-info-visualizer