• Stan Hu's avatar
    Fix slow performance with compiling HAML templates · b02458ef
    Stan Hu authored
    In Rails 5, including `ActionView::Context` can have a significant and
    hidden performance penalty because this module also includes
    `ActionView::CompiledTemplates`. This means that any module that
    includes ActionView::Context becomes a descendant of
    `CompiledTemplates`.
    
    When a partial is rendered for the first time, it runs
    `ActionView::CompiledTemplates#module_eval`, which will evaluate a
    string that defines a new method for that partial. For example, the
    source of partial might be this string:
    
    ```
    def _app_views_project_show_html_haml___12345(local_assigns, output)
      "hello world"
    end
    ```
    
    When this string is evaluated, the Ruby interpreter will define the
    method and clear the global method cache for all descendants of
    `ActionView::CompiledTemplates`. Previous to this change, we
    inadvertently made a number of modules fall into this category:
    
    * GroupChildEntity
    * NoteUserEntity
    * Notify
    * MergeRequestUserEntity
    * AnalyticsCommitEntity
    * CommitEntity
    * UserEntity
    * Kaminari::Helpers::Paginator
    * CurrentUserEntity
    * ActionView::Base
    * ActionDispatch::DebugExceptions::DebugView
    * MarkupHelper
    * MergeRequestPresenter
    
    After this change:
    
    * Kaminari::Helpers::Paginator
    * ActionView::Base
    * ActionDispatch::DebugExceptions::DebugView
    
    Each time a partial is rendered for the first time, all methods for
    those modules will have to be redefined. This can exact a significant
    performance penalty.
    
    How bad is this penalty? Using the following benchmark script, we can
    use DTrace to sample the Ruby interpreter:
    
    ```
    Benchmark.bm do |x|
      x.report do
        1000.times do
          ActionView::CompiledTemplates.module_eval("def testme\nend")
        end
      end
    end
    ```
    
    This revealed a 11x jump in the time spent in `core#define_method`
    alone.
    
    Rails 6 fixes this behavior by moving the `include CompiledTemplates`
    into ActionView::Base so that including `ActionView::Context` doesn't
    quietly affect other modules in this way.
    
    Closes https://gitlab.com/gitlab-org/gitlab-ee/issues/11198
    b02458ef