systemjs / systemjs

Dynamic ES module loader
MIT License
12.94k stars 1.09k forks source link

System.register bundle slower than the CommonJS one? #908

Closed laurentgoudet closed 8 years ago

laurentgoudet commented 8 years ago

Imported from https://github.com/angular/angular/issues/5196.

This plunker is a fork of the "original" Angular plunker, only the Angular version has been updated to the latest (2.0.0-alpha.45, unminifed).

This other fork is based on the first one, expect that the angular2 bundle has been generated by the System builder through jspm bundle angular2/angular2 + reflect-metadata (version 0.16.14, see logs below).

For some reason, the later is noticeably slower to bootstrap than the former: "loading..." is shown for several seconds while that the same Angular app bootstrap almost instantaneously in the first plunker. Both bundles have roughly the same size (~1MB).

I see the same issue in my local test app: swapping the System builder bundle (constructed through tracing angular2 ES6 imports) for the distributed bundle (jspm_packages/npm/angular2@2.0.0-alpha.45/bundles/angular2.dev.js) saves several seconds of load time.

After further testing - I was trying to look at the timeline - I've realized that it only happens when the Chrome DevTools are opened 0_o. I've grabbed a screencast of the issue https://youtu.be/e1cTPIDCCZE, which I can reproduce using the previous plunkers in both Chrome stable v46 and Canary v48. Even worse, trying the capture a timeline "fixes" the issue: no more visible lag with the System builder's bundle.

I had Async debugging enabled in the DevTools, which makes the issue way worse. Without it, the System.register bundle is still consistently 30% to 50% slower than the CommonJS one (1s vs. 1.5s fully loaded), only when the DevTools are opened (JS source maps are disabled).

My first guess would be that it's due to the System.register circular reference "correctness" overhead, however I can't explain why I'm only seeing that when the DevTools are enabled. Maybe the System.register format lead to deeper call stacks which makes the JS instrumentation more costly.

laurent@MacBook-Pro app [master] $ jspm bundle angular2/angular2 + reflect-metadata
     Building the bundle tree for angular2/angular2 + reflect-metadata...

       github:jspm/nodelibs-process@0.1.2
       github:jspm/nodelibs-process@0.1.2/index
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/Observable
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/Subject
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/Subscriber
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/Subscription
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/subjects/SubjectSubscription
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/util/Symbol_observable
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/util/noop
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/util/root
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/util/throwError
       npm:@reactivex/rxjs@5.0.0-alpha.4/dist/cjs/util/tryOrOnError
       npm:angular2@2.0.0-alpha.45/angular2
       npm:angular2@2.0.0-alpha.45/bootstrap
       npm:angular2@2.0.0-alpha.45/core
       npm:angular2@2.0.0-alpha.45/lifecycle_hooks
       npm:angular2@2.0.0-alpha.45/profile
       npm:angular2@2.0.0-alpha.45/render
       npm:angular2@2.0.0-alpha.45/src/animate/animation
       npm:angular2@2.0.0-alpha.45/src/animate/animation_builder
       npm:angular2@2.0.0-alpha.45/src/animate/browser_details
       npm:angular2@2.0.0-alpha.45/src/animate/css_animation_builder
       npm:angular2@2.0.0-alpha.45/src/animate/css_animation_options
       npm:angular2@2.0.0-alpha.45/src/core/application
       npm:angular2@2.0.0-alpha.45/src/core/application_common
       npm:angular2@2.0.0-alpha.45/src/core/application_ref
       npm:angular2@2.0.0-alpha.45/src/core/application_tokens
       npm:angular2@2.0.0-alpha.45/src/core/bootstrap
       npm:angular2@2.0.0-alpha.45/src/core/change_detection
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/abstract_change_detector
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/binding_record
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/change_detection
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/change_detection_jit_generator
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/change_detection_util
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/change_detector_ref
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/coalesce
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/codegen_facade
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/codegen_logic_util
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/codegen_name_util
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/constants
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/differs/default_iterable_differ
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/differs/default_keyvalue_differ
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/differs/iterable_differs
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/differs/keyvalue_differs
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/directive_record
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/dynamic_change_detector
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/event_binding
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/exceptions
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/interfaces
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/jit_proto_change_detector
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/observable_facade
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/parser/ast
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/parser/lexer
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/parser/locals
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/parser/parser
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/pipe_lifecycle_reflector
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/pipes
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/proto_change_detector
       npm:angular2@2.0.0-alpha.45/src/core/change_detection/proto_record
       npm:angular2@2.0.0-alpha.45/src/core/compiler/anchor_based_app_root_url
       npm:angular2@2.0.0-alpha.45/src/core/compiler/app_root_url
       npm:angular2@2.0.0-alpha.45/src/core/compiler/change_definition_factory
       npm:angular2@2.0.0-alpha.45/src/core/compiler/change_detector_compiler
       npm:angular2@2.0.0-alpha.45/src/core/compiler/command_compiler
       npm:angular2@2.0.0-alpha.45/src/core/compiler/compiler
       npm:angular2@2.0.0-alpha.45/src/core/compiler/directive_metadata
       npm:angular2@2.0.0-alpha.45/src/core/compiler/html_ast
       npm:angular2@2.0.0-alpha.45/src/core/compiler/html_parser
       npm:angular2@2.0.0-alpha.45/src/core/compiler/runtime_compiler
       npm:angular2@2.0.0-alpha.45/src/core/compiler/runtime_metadata
       npm:angular2@2.0.0-alpha.45/src/core/compiler/schema/dom_element_schema_registry
       npm:angular2@2.0.0-alpha.45/src/core/compiler/schema/element_schema_registry
       npm:angular2@2.0.0-alpha.45/src/core/compiler/selector
       npm:angular2@2.0.0-alpha.45/src/core/compiler/shadow_css
       npm:angular2@2.0.0-alpha.45/src/core/compiler/source_module
       npm:angular2@2.0.0-alpha.45/src/core/compiler/style_compiler
       npm:angular2@2.0.0-alpha.45/src/core/compiler/style_url_resolver
       npm:angular2@2.0.0-alpha.45/src/core/compiler/template_ast
       npm:angular2@2.0.0-alpha.45/src/core/compiler/template_compiler
       npm:angular2@2.0.0-alpha.45/src/core/compiler/template_normalizer
       npm:angular2@2.0.0-alpha.45/src/core/compiler/template_parser
       npm:angular2@2.0.0-alpha.45/src/core/compiler/template_preparser
       npm:angular2@2.0.0-alpha.45/src/core/compiler/url_resolver
       npm:angular2@2.0.0-alpha.45/src/core/compiler/util
       npm:angular2@2.0.0-alpha.45/src/core/compiler/xhr
       npm:angular2@2.0.0-alpha.45/src/core/compiler/xhr_impl
       npm:angular2@2.0.0-alpha.45/src/core/debug
       npm:angular2@2.0.0-alpha.45/src/core/debug/debug_element
       npm:angular2@2.0.0-alpha.45/src/core/debug/debug_element_view_listener
       npm:angular2@2.0.0-alpha.45/src/core/di
       npm:angular2@2.0.0-alpha.45/src/core/di/decorators
       npm:angular2@2.0.0-alpha.45/src/core/di/exceptions
       npm:angular2@2.0.0-alpha.45/src/core/di/forward_ref
       npm:angular2@2.0.0-alpha.45/src/core/di/injector
       npm:angular2@2.0.0-alpha.45/src/core/di/key
       npm:angular2@2.0.0-alpha.45/src/core/di/metadata
       npm:angular2@2.0.0-alpha.45/src/core/di/opaque_token
       npm:angular2@2.0.0-alpha.45/src/core/di/provider
       npm:angular2@2.0.0-alpha.45/src/core/di/type_literal
       npm:angular2@2.0.0-alpha.45/src/core/directives
       npm:angular2@2.0.0-alpha.45/src/core/directives/ng_class
       npm:angular2@2.0.0-alpha.45/src/core/directives/ng_for
       npm:angular2@2.0.0-alpha.45/src/core/directives/ng_if
       npm:angular2@2.0.0-alpha.45/src/core/directives/ng_style
       npm:angular2@2.0.0-alpha.45/src/core/directives/ng_switch
       npm:angular2@2.0.0-alpha.45/src/core/directives/observable_list_diff
       npm:angular2@2.0.0-alpha.45/src/core/dom/browser_adapter
       npm:angular2@2.0.0-alpha.45/src/core/dom/dom_adapter
       npm:angular2@2.0.0-alpha.45/src/core/dom/generic_browser_adapter
       npm:angular2@2.0.0-alpha.45/src/core/facade
       npm:angular2@2.0.0-alpha.45/src/core/facade/async
       npm:angular2@2.0.0-alpha.45/src/core/facade/collection
       npm:angular2@2.0.0-alpha.45/src/core/facade/exception_handler
       npm:angular2@2.0.0-alpha.45/src/core/facade/exceptions
       npm:angular2@2.0.0-alpha.45/src/core/facade/intl
       npm:angular2@2.0.0-alpha.45/src/core/facade/lang
       npm:angular2@2.0.0-alpha.45/src/core/facade/math
       npm:angular2@2.0.0-alpha.45/src/core/facade/promise
       npm:angular2@2.0.0-alpha.45/src/core/forms
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/abstract_control_directive
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/checkbox_value_accessor
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/control_container
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/control_value_accessor
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/default_value_accessor
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_control
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_control_group
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_control_name
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_control_status
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_form
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_form_control
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_form_model
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/ng_model
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/normalize_validator
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/number_value_accessor
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/select_control_value_accessor
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/shared
       npm:angular2@2.0.0-alpha.45/src/core/forms/directives/validators
       npm:angular2@2.0.0-alpha.45/src/core/forms/form_builder
       npm:angular2@2.0.0-alpha.45/src/core/forms/model
       npm:angular2@2.0.0-alpha.45/src/core/forms/validators
       npm:angular2@2.0.0-alpha.45/src/core/life_cycle/life_cycle
       npm:angular2@2.0.0-alpha.45/src/core/lifecycle
       npm:angular2@2.0.0-alpha.45/src/core/linker
       npm:angular2@2.0.0-alpha.45/src/core/linker/compiler
       npm:angular2@2.0.0-alpha.45/src/core/linker/directive_lifecycle_reflector
       npm:angular2@2.0.0-alpha.45/src/core/linker/directive_resolver
       npm:angular2@2.0.0-alpha.45/src/core/linker/dynamic_component_loader
       npm:angular2@2.0.0-alpha.45/src/core/linker/element_binder
       npm:angular2@2.0.0-alpha.45/src/core/linker/element_injector
       npm:angular2@2.0.0-alpha.45/src/core/linker/element_ref
       npm:angular2@2.0.0-alpha.45/src/core/linker/event_config
       npm:angular2@2.0.0-alpha.45/src/core/linker/interfaces
       npm:angular2@2.0.0-alpha.45/src/core/linker/pipe_resolver
       npm:angular2@2.0.0-alpha.45/src/core/linker/proto_view_factory
       npm:angular2@2.0.0-alpha.45/src/core/linker/query_list
       npm:angular2@2.0.0-alpha.45/src/core/linker/template_commands
       npm:angular2@2.0.0-alpha.45/src/core/linker/template_ref
       npm:angular2@2.0.0-alpha.45/src/core/linker/view
       npm:angular2@2.0.0-alpha.45/src/core/linker/view_container_ref
       npm:angular2@2.0.0-alpha.45/src/core/linker/view_listener
       npm:angular2@2.0.0-alpha.45/src/core/linker/view_manager
       npm:angular2@2.0.0-alpha.45/src/core/linker/view_manager_utils
       npm:angular2@2.0.0-alpha.45/src/core/linker/view_pool
       npm:angular2@2.0.0-alpha.45/src/core/linker/view_ref
       npm:angular2@2.0.0-alpha.45/src/core/linker/view_resolver
       npm:angular2@2.0.0-alpha.45/src/core/metadata
       npm:angular2@2.0.0-alpha.45/src/core/metadata/di
       npm:angular2@2.0.0-alpha.45/src/core/metadata/directives
       npm:angular2@2.0.0-alpha.45/src/core/metadata/view
       npm:angular2@2.0.0-alpha.45/src/core/pipes
       npm:angular2@2.0.0-alpha.45/src/core/pipes/async_pipe
       npm:angular2@2.0.0-alpha.45/src/core/pipes/date_pipe
       npm:angular2@2.0.0-alpha.45/src/core/pipes/default_pipes
       npm:angular2@2.0.0-alpha.45/src/core/pipes/invalid_pipe_argument_exception
       npm:angular2@2.0.0-alpha.45/src/core/pipes/json_pipe
       npm:angular2@2.0.0-alpha.45/src/core/pipes/lowercase_pipe
       npm:angular2@2.0.0-alpha.45/src/core/pipes/number_pipe
       npm:angular2@2.0.0-alpha.45/src/core/pipes/pipe_provider
       npm:angular2@2.0.0-alpha.45/src/core/pipes/pipes
       npm:angular2@2.0.0-alpha.45/src/core/pipes/slice_pipe
       npm:angular2@2.0.0-alpha.45/src/core/pipes/uppercase_pipe
       npm:angular2@2.0.0-alpha.45/src/core/platform_bindings
       npm:angular2@2.0.0-alpha.45/src/core/profile/profile
       npm:angular2@2.0.0-alpha.45/src/core/profile/wtf_impl
       npm:angular2@2.0.0-alpha.45/src/core/profile/wtf_init
       npm:angular2@2.0.0-alpha.45/src/core/reflection/reflection
       npm:angular2@2.0.0-alpha.45/src/core/reflection/reflection_capabilities
       npm:angular2@2.0.0-alpha.45/src/core/reflection/reflector
       npm:angular2@2.0.0-alpha.45/src/core/render
       npm:angular2@2.0.0-alpha.45/src/core/render/api
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/dom_renderer
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/dom_tokens
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/events/event_manager
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/events/hammer_common
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/events/hammer_gestures
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/events/key_events
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/shared_styles_host
       npm:angular2@2.0.0-alpha.45/src/core/render/dom/util
       npm:angular2@2.0.0-alpha.45/src/core/render/render
       npm:angular2@2.0.0-alpha.45/src/core/render/view
       npm:angular2@2.0.0-alpha.45/src/core/render/view_factory
       npm:angular2@2.0.0-alpha.45/src/core/services
       npm:angular2@2.0.0-alpha.45/src/core/services/title
       npm:angular2@2.0.0-alpha.45/src/core/testability/browser_testability
       npm:angular2@2.0.0-alpha.45/src/core/testability/testability
       npm:angular2@2.0.0-alpha.45/src/core/util
       npm:angular2@2.0.0-alpha.45/src/core/util/decorators
       npm:angular2@2.0.0-alpha.45/src/core/zone
       npm:angular2@2.0.0-alpha.45/src/core/zone/ng_zone
       npm:angular2@2.0.0-alpha.45/src/transform/template_compiler/change_detector_codegen
       npm:process@0.11.2
       npm:process@0.11.2/browser
       npm:reflect-metadata@0.1.2
       npm:reflect-metadata@0.1.2/Reflect

ok   Built into build.js with source maps, unminified.
guybedford commented 8 years ago

See also https://github.com/JonahBraun/jspm-perf-test for related performance benchmarks here.

guybedford commented 8 years ago

I'm wondering if its worth posting a devtools bug for this.

laurentgoudet commented 8 years ago

Something SystemJS does is definitely not playing nice with the Chrome DevTools. While playing with the CPU profiler, I've realized that:

When looking at chrome://tracing it seems that the complete(evt) callback takes 3x more time with the profiler is off (314ms to 939ms):

screen shot 2015-11-11 at 11 36 03 am screen shot 2015-11-11 at 11 32 29 am
robwormald commented 8 years ago

one point to mention - the distributed angular2 bundles are still using quite an old version of the System bundler - i believe its 10.0.3 - so perhaps something weird has happened in the interim.

guybedford commented 8 years ago

@laurentgoudet so to confirm, are you saying that the bundle at https://s3.amazonaws.com/angular2-bug/build.js is slower than the bundle at https://code.angularjs.org/2.0.0-alpha.45/angular2.js when devtools is open?

Note that the first bundle is a System.registerDynamic bundle not a System.register which is an important distinction here as well.

laurentgoudet commented 8 years ago

Yeah looking at it in http://embed.plnkr.co/Q3tmBtiBVjQmcEHqcFoF/preview and http://embed.plnkr.co/cgcaaCISIheWSv5jPAGi/preview it does seem that System.register performs better than System.registerDynamic when the DevTools are openned.

On a side note angular2 is still alpha and not really playing nice SystemJS: for instance, dynamically loading the router through SystemJS (as modules or as a bundle) silently breaks the framework (no more binding resolution or anything but no error), so I'd be wary of the side effects until it reaches beta quality.

guybedford commented 8 years ago

Interesting to know. System.registerDynamic has much deeper execution stacks by its nature of handling CommonJS stack-based execution, which may go some way to explaining it.

Thanks for mentioning - I believe angular2 are looking to support SystemJS workflows, so it may just be a case of reporting these sorts of issues to ensure they can work out correctly.

robwormald commented 8 years ago

Innnteresting. Does that mean it would be preferable for end users to build from the ES6 distribution?

On Nov 16, 2015, at 3:07 AM, Guy Bedford notifications@github.com wrote:

Interesting to know. System.registerDynamic has much deeper execution stacks by its nature of handling CommonJS stack-based execution, which may go some way to explaining it.

Thanks for mentioning - I believe angular2 are looking to support SystemJS workflows, so it may just be a case of reporting these sorts of issues to ensure they can work out correctly.

— Reply to this email directly or view it on GitHub https://github.com/systemjs/systemjs/issues/908#issuecomment-156996138.

guybedford commented 8 years ago

I'm not sure it's a good enough reason on its own for using the ES6 directly, but if there are other arguments it can be worth considering. Since this is a devtools thing it could be worth waiting to see if browsers optimize this further in due course as well.