How to analyze compile performance

On the first week of September 2018 we encountered slow compile times for the VaaS Web app. This page will explain how to identify compilation bottlenecks and if you want you can git checkout and follow the same steps for analyzing compile performance.

git checkout 3bd3cc9bb2a9eefe5c1363829f03d60c18286d18

Fist navigate to the root of the VaaS repository and run this command in the terminal.

mix xref graph

The result of this command is a dependency graph between modules/protcols/structs etc. For the VaaS Core App the tree was quite flat and that is how we should strive to write our apps. Unfortunately for Vaas Web it is a different case and we encountered some circular dependencies caused by vaas_web.ex and certain views. See the output with the problem circular dependencies below:

lib/vaas_web/protocols/to_social_data.ex
├── lib/vaas_web/routers/vaas_router.ex (compile)
│   ├── lib/vaas_web.ex (compile)
│   ├── lib/vaas_web/plugs/auth_plug.ex (compile)
│   ├── lib/vaas_web/plugs/external_user_plug.ex (compile)
│   ├── lib/vaas_web/plugs/layout_plug.ex (compile)
│   │   └── lib/vaas_web/views/layout_view.ex
│   │       ├── lib/vaas_web.ex (compile)
│   │       ├── lib/vaas_web/gettext.ex (compile)
│   │       ├── lib/vaas_web/helpers/client_side_datastore_helpers.ex (compile)
│   │       │   ├── lib/vaas_web/helpers/view_helpers.ex
│   │       │   │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │   │   │   ├── lib/vaas_web/gettext.ex
│   │       │   │   │   └── lib/vaas_web/helpers/conn_helpers.ex (compile)
│   │       │   │   └── lib/vaas_web/views/imgix_helpers.ex
│   │       │   ├── lib/vaas_web/views/imgix_helpers.ex
│   │       │   └── lib/vaas_web/views/theoplayer_helpers.ex
│   │       │       ├── lib/vaas_web/helpers/view_helpers.ex
│   │       │       ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       └── lib/vaas_web/views/imgix_helpers.ex
│   │       ├── lib/vaas_web/helpers/feature_flag_helpers.ex (compile)
│   │       ├── lib/vaas_web/helpers/view_helpers.ex (compile)
│   │       ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │   └── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       ├── lib/vaas_web/social_data_tag_helpers.ex (compile)
│   │       │   ├── lib/vaas_web/protocols/to_social_data.ex
│   │       │   └── lib/vaas_web/sites/social_data.ex
│   │       │       ├── lib/vaas_web/helpers/conn_helpers.ex
│   │       │       └── lib/vaas_web/plugs/wall_configuration_plug.ex
│   │       ├── lib/vaas_web/views/error_helpers.ex (compile)
│   │       │   └── lib/vaas_web/gettext.ex
│   │       ├── lib/vaas_web/views/imgix_helpers.ex (compile)
│   │       ├── lib/vaas_web/views/stitch_helpers.ex (compile)
│   │       │   ├── lib/vaas_web/endpoint.ex
│   │       │   │   ├── lib/vaas_web/plugs/cors_router.ex (compile)
│   │       │   │   └── lib/vaas_web/router.ex (compile)
│   │       │   │       ├── lib/vaas_web.ex (compile)
│   │       │   │       ├── lib/vaas_web/routers/api_router.ex (compile)
│   │       │   │       ├── lib/vaas_web/routers/dfp_feed_router.ex (compile)
│   │       │   │       ├── lib/vaas_web/routers/embed_api_router.ex (compile)
│   │       │   │       │   ├── lib/vaas_web.ex (compile)
│   │       │   │       │   └── lib/vaas_web/plugs/auth_plug.ex (compile)
│   │       │   │       ├── lib/vaas_web/routers/gravity_router.ex (compile)
│   │       │   │       └── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │   ├── lib/vaas_web/plugs/set_page_type_plug.ex
│   │       │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex
│   │       │   ├── lib/vaas_web/router.ex (compile)
│   │       │   ├── lib/vaas_web/social_data_tag_helpers.ex (compile)
│   │       │   └── lib/vaas_web/views/shared_view.ex
│   │       │       ├── lib/vaas_web.ex (compile)
│   │       │       ├── lib/vaas_web/controllers/channel/show_controller.ex
│   │       │       │   ├── lib/vaas_web.ex (compile)
│   │       │       │   ├── lib/vaas_web/controllers/fallback_controller.ex (compile)
│   │       │       │   │   ├── lib/vaas_web.ex (compile)
│   │       │       │   │   ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │   │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │   │   ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │   │   ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │   │   └── lib/vaas_web/views/error_view.ex
│   │       │       │   │       ├── lib/vaas_web.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/helpers/client_side_datastore_helpers.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/helpers/feature_flag_helpers.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/helpers/view_helpers.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/social_data_tag_helpers.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/views/error_helpers.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/views/imgix_helpers.ex (compile)
│   │       │       │   │       ├── lib/vaas_web/views/stitch_helpers.ex (compile)
│   │       │       │   │       └── lib/vaas_web/views/theoplayer_helpers.ex (compile)
│   │       │       │   ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │   ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │   ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │   └── lib/vaas_web/views/show_production_view.ex
│   │       │       │       ├── lib/vaas_web.ex (compile)
│   │       │       │       ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │       ├── lib/vaas_web/helpers/client_side_datastore_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/helpers/feature_flag_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/helpers/view_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │       ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │       ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/social_data_tag_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/error_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/imgix_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/shared_view.ex
│   │       │       │       ├── lib/vaas_web/views/stitch_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/theoplayer_helpers.ex (compile)
│   │       │       │       └── lib/vaas_web/views/wall_view.ex
│   │       │       │           ├── lib/vaas_web.ex (compile)
│   │       │       │           ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │           ├── lib/vaas_web/helpers/client_side_datastore_helpers.ex (compile)
│   │       │       │           ├── lib/vaas_web/helpers/feature_flag_helpers.ex (compile)
│   │       │       │           ├── lib/vaas_web/helpers/view_helpers.ex (compile)
│   │       │       │           ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │           ├── lib/vaas_web/router.ex (compile)
│   │       │       │           ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │           ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │           ├── lib/vaas_web/social_data_tag_helpers.ex (compile)
│   │       │       │           ├── lib/vaas_web/views/error_helpers.ex (compile)
│   │       │       │           ├── lib/vaas_web/views/imgix_helpers.ex (compile)
│   │       │       │           ├── lib/vaas_web/views/shared_view.ex
│   │       │       │           ├── lib/vaas_web/views/stitch_helpers.ex (compile)
│   │       │       │           └── lib/vaas_web/views/theoplayer_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/controllers/production_controller.ex
│   │       │       │   ├── lib/vaas_web.ex (compile)
│   │       │       │   ├── lib/vaas_web/controllers/fallback_controller.ex (compile)
│   │       │       │   ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │   ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │   ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │   └── lib/vaas_web/views/show_production_view.ex (compile)
│   │       │       ├── lib/vaas_web/controllers/show_controller.ex
│   │       │       │   ├── lib/vaas_web.ex (compile)
│   │       │       │   ├── lib/vaas_web/controllers/fallback_controller.ex (compile)
│   │       │       │   ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │   ├── lib/vaas_web/helpers/offset_helpers.ex (compile)
│   │       │       │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │   ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │   ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │   └── lib/vaas_web/views/show_view.ex
│   │       │       │       ├── lib/vaas_web.ex (compile)
│   │       │       │       ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │       ├── lib/vaas_web/helpers/client_side_datastore_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/helpers/feature_flag_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/helpers/view_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │       ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │       ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/social_data_tag_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/error_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/imgix_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/shared_view.ex
│   │       │       │       ├── lib/vaas_web/views/stitch_helpers.ex (compile)
│   │       │       │       ├── lib/vaas_web/views/theoplayer_helpers.ex (compile)
│   │       │       │       └── lib/vaas_web/views/wall_view.ex
│   │       │       ├── lib/vaas_web/controllers/wall_controller.ex
│   │       │       │   ├── lib/vaas_web.ex (compile)
│   │       │       │   ├── lib/vaas_web/controllers/fallback_controller.ex (compile)
│   │       │       │   ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │   ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │   ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       │   └── lib/vaas_web/views/wall_view.ex
│   │       │       ├── lib/vaas_web/controllers/watchlist_controller.ex
│   │       │       │   ├── lib/vaas_web.ex (compile)
│   │       │       │   ├── lib/vaas_web/controllers/fallback_controller.ex (compile)
│   │       │       │   ├── lib/vaas_web/gettext.ex (compile)
│   │       │       │   ├── lib/vaas_web/helpers/offset_helpers.ex (compile)
│   │       │       │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       │   ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       │   └── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/gettext.ex (compile)
│   │       │       ├── lib/vaas_web/helpers/client_side_datastore_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/helpers/feature_flag_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/helpers/view_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │       │       ├── lib/vaas_web/routers/vaas_router.ex (compile)
│   │       │       ├── lib/vaas_web/slug_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/social_data_tag_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/views/error_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/views/imgix_helpers.ex (compile)
│   │       │       ├── lib/vaas_web/views/stitch_helpers.ex (compile)
│   │       │       └── lib/vaas_web/views/theoplayer_helpers.ex (compile)
│   │       └── lib/vaas_web/views/theoplayer_helpers.ex (compile)
│   ├── lib/vaas_web/plugs/log_request_headers_plug.ex (compile)
│   ├── lib/vaas_web/plugs/redirect_videopath_plug.ex (compile)
│   │   ├── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
│   │   └── lib/vaas_web/routers/vaas_router.ex
│   ├── lib/vaas_web/plugs/set_api_user_plug.ex (compile)
│   ├── lib/vaas_web/plugs/set_page_type_plug.ex (compile)
│   └── lib/vaas_web/plugs/wall_configuration_plug.ex (compile)
├── lib/vaas_web/sites/social_data.ex (struct)
├── lib/vaas_web/slug_helpers.ex (compile)
└── lib/vaas_web/views/imgix_helpers.ex (compile)

If you run mix compile you will see how many files it will compile, which is helpful. However you would like to know which specific files do re-compile and with such a nested dependency graph it is quite difficult to see this. So fswatch to the rescue! From the root of the folder run the following command (notice that this is specifically targeted for vaas_web, but we could also change the target to vaas_core if we want to):

fswatch --event="Created" _build/dev/lib/vaas_web/ebin/

When we change some pug template in the wall view, this will trigger 57 files to re-compile (normally this should only be less than 5 files in the worst case). If we look at the output we can see which files re-compiled (normally you would get the paths of the changed files, but I removed the paths for better readability)

Elixir.VaasWeb.WallView.beam
Elixir.VaasWeb.Sanity.SanityView.beam
Elixir.VaasWeb.Protocols.ToSocialData.VaasCore.Sites.WallConfiguration.beam
Elixir.VaasWeb.ChannelView.beam
Elixir.VaasWeb.Protocols.ToSocialData.VaasCore.AccessibleContent.Show.beam
Elixir.VaasWeb.Protocols.ToSocialData.VaasCore.AccessibleContent.Production.beam
Elixir.VaasWeb.Protocols.ToSocialData.VaasCore.AccessibleContent.Channel.beam
Elixir.VaasWeb.Protocols.ToSocialData.beam
Elixir.VaasWeb.Api.SocialShareUrl.beam
Elixir.VaasWeb.WatchlistController.beam
Elixir.VaasWeb.Api.V2.FallbackController.beam
Elixir.VaasWeb.Channel.ShowController.beam
Elixir.VaasWeb.ShowController.beam
Elixir.VaasWeb.ChannelController.beam
Elixir.VaasWeb.RecommendationController.beam
Elixir.VaasWeb.EmbedApi.V1.ProductionController.beam
Elixir.VaasWeb.Embed.EmbedView.beam
Elixir.VaasWeb.SharedView.beam
Elixir.VaasWeb.RecommendationView.beam
Elixir.VaasWeb.Channel.ShowView.beam
Elixir.VaasWeb.WatchlistView.beam
Elixir.VaasWeb.ProductionView.beam
Elixir.VaasWeb.PlaylistView.beam
Elixir.VaasWeb.ShowView.beam
Elixir.VaasWeb.ChangesetView.beam
Elixir.VaasWeb.ErrorView.beam
Elixir.VaasWeb.ShowProductionController.beam
Elixir.VaasWeb.ProductionController.beam
Elixir.VaasWeb.Endpoint.beam
Elixir.VaasWeb.ShowProductionView.beam
Elixir.VaasWeb.Sanity.SanityController.beam
Elixir.VaasWeb.Feed.DFPVideo.beam
Elixir.VaasWeb.Router.beam
Elixir.VaasWeb.RootController.beam
Elixir.VaasWeb.StitchHelpers.beam
Elixir.VaasWeb.EmbedApi.V1.FallbackController.beam
Elixir.VaasWeb.BookmarkController.beam
Elixir.VaasWeb.ImageRedirectController.beam
Elixir.VaasWeb.Router.Helpers.beam
Elixir.VaasWeb.WallController.beam
Elixir.VaasWeb.EmbedApi.V1.PlaylistController.beam
Elixir.VaasWeb.VaasRouter.beam
Elixir.VaasWeb.PlaylistController.beam
Elixir.VaasWeb.Embed.EmbedController.beam
Elixir.VaasWeb.FallbackController.beam
Elixir.VaasWeb.SlugHelpers.beam
Elixir.VaasWeb.VaasRouter.Helpers.beam
Elixir.VaasWeb.Api.V2.Show.ProductionController.beam
Elixir.VaasWeb.Api.V2.Wall.BannerView.beam
Elixir.VaasWeb.Api.V2.ShowView.beam
Elixir.VaasWeb.Api.V2.Wall.PlaylistView.beam
Elixir.VaasWeb.Api.V2.PlaylistController.beam
Elixir.VaasWeb.Api.V2.ProductionView.beam
Elixir.VaasWeb.LayoutView.beam
Elixir.VaasWeb.Api.V2.ShowController.beam
Elixir.VaasWeb.Api.V2.ChannelController.beam
Elixir.VaasWeb.Api.V2.Wall.BannerController.beam
Elixir.VaasWeb.Api.V2.Wall.PlaylistController.beam
Elixir.VaasWeb.Api.V2.ProductionController.beam
Elixir.VaasWeb.Api.V2.ChannelView.beam
Elixir.VaasWeb.Api.V2.WallController.beam
Elixir.VaasWeb.Api.V2.Channel.ShowController.beam
Elixir.VaasWeb.Api.V2.Playlist.ProductionController.beam

If we analyze the results we see that for a small change in one view it triggers 18 other views, 29 controllers, 4 helpers, 2 routers and 5 protocol related file re-compilations. The main issues are caused by imports of our own modules, unnecessary macro's, protocol with structs (structs get always checked during compile time) and circular dependencies, which re-triggers compilation of linked files.

The solution is by using aliases instead of imports, whenever possible. You should only use imports when you are importing something from an external dependency and preferably use this the following way:

import ExternalDeps, only: [some_function_with_arity: 2]

Another good thing is not to write macro's if they are not necessary. See Elixir's own documentation considers macro's being used while they could have been written as normal functions as bad style!.

In the case of circular dependencies in VaasWeb.Api.V2 it is good to understand how this is happening.

  VaasWeb.Api.V2.ProductionView -> VaasWeb.Api.V2.ShowView -> VaasWeb.Api.V2.ChannelView -> VaasWeb.Api.V2.ProductionView

This is caused by the serializer of the JaSerializer.PhoenixView module. Renan Ranelli has a good blogpost and talk about understanding compilation issues. This case can be solved by using Module.concat(["Namespace.ModuleName"]). However we should use this with care because it can make debugging harder in the specific code modules whenever used.

At the end we went from 57 (re)compilations for changing a random pug template to 5 (re)compilations and this is because of the to_social.ex protocol with some structs from vaas_core. No solution for that protocol yet, but it is negligible for the compile performance.

Last Updated: 2/28/2019, 3:20:56 PM