Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beyond Ludicrous Speed #21057

Merged
merged 22 commits into from Jul 30, 2015
Merged

Commits on Jul 30, 2015

  1. Decrease string allocations on AR#respond_to?

    When a symbol is passed in, we call `to_s` on it which allocates a string. The two hardcoded symbols that are used internally are `:to_partial_path` and `:to_model`.
    
    This change buys us 71,136 bytes of memory and 1,777 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    f80aa59 View commit details
    Browse the repository at this point in the history
  2. Decrease string allocations in apply_inflections

    In `apply_inflections` a string is down cased and some whitespace stripped in the front (which allocate strings). This would normally be fine, however `uncountables` is a fairly small array (10 elements out of the box) and this method gets called a TON. Instead we can keep an array of valid regexes for each uncountable so we don't have to allocate new strings.
    
    This change buys us 325,106 bytes of memory and 3,251 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    1bf50ba View commit details
    Browse the repository at this point in the history
  3. Decrease string allocations in url_options

    The request.script_name is dup-d which allocates an extra string. It is most commonly an empty string "". We can save a ton of string allocations by checking first if the string is empty, if so we can use a frozen empty string instead of duplicating an empty string.
    
    This change buys us 35,714 bytes of memory and 893 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    83ee043 View commit details
    Browse the repository at this point in the history
  4. Speed up journey extract_parameterized_parts

    Micro optimization: `reverse.drop_while` is slower than `reverse_each.drop_while`. This doesn't save any object allocations.
    
    Second, `keys_to_keep` is typically a very small array. The operation `parameterized_parts.keys - keys_to_keep` actually allocates two arrays. It is quicker (I benchmarked) to iterate over each and check inclusion in array manually.
    
    This change buys us 1774 fewer objects per request
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    9b82588 View commit details
    Browse the repository at this point in the history
  5. Speed up journey missing_keys

    Most routes have a `route.path.requirements[key]` of `/[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/` yet every time this method is called a new regex is generated on the fly with `/\A#{DEFAULT_INPUT}\Z/`. OBJECT ALLOCATIONS BLERG!
    
    This change uses a special module that implements `===` so it can be used in a case statement to pull out the default input. When this happens, we use a pre-generated regex.
    
    This change buys us 1,643,465 bytes of memory and 7,990 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    097ec6f View commit details
    Browse the repository at this point in the history
  6. Decrease route_set allocations

    In handle_positional_args `Array#-=` is used which allocates a new array. Instead we can iterate through and delete elements, modifying the array in place.
    
    Also `Array#take` allocates a new array. We can build the same by iterating over the other element.
    
    This change buys us 106,470 bytes of memory and 2,663 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    0cbec58 View commit details
    Browse the repository at this point in the history
  7. Reduce hash allocations in route_set

    When generating a url with `url_for` the hash of arguments passed in, is dup-d and merged a TON. I wish I could clean this up better, and might be able to do it in the future. This change removes one dup, since it's literally right after we just dup-d the hash to pass into this constructor.
    
    This may be a breaking, change but the tests pass...so :shipit: we can revert if it causes problems
    
    This change buys us 205,933 bytes of memory and 887 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    1a14074 View commit details
    Browse the repository at this point in the history
  8. Decrease string allocation in content_tag_string

    When an unknonwn key is passed to the hash in `PRE_CONTENT_STRINGS` it returns nil, when you call "#{nil}" it allocates a new empty string. We can get around this allocation by using a default value `Hash.new { "".freeze }`. We can avoid the `to_sym` call by pre-populating the hash with a symbol key in addition to a string key.
    
    We can freeze some strings when using Array#* to reduce allocations.
    
    Array#join can take frozen strings.
    
    This change buys us 86,600 bytes of memory and 1,857 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    2a4d430 View commit details
    Browse the repository at this point in the history
  9. Optimize hash key

    No idea why on earth this hash key isn't already optimized by MRI, but it isn't. 💩
    
    This change buys us 74,077 bytes of memory and 1,852 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    2e95d2e View commit details
    Browse the repository at this point in the history
  10. Cut string ActionView template allocations

    The instrument method creates new strings, the most common action to instrument is "!render_template` so we can detect when that action is occurring and use a frozen string instead.
    
    This change buys us 113,714 bytes of memory and 1,790 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    9b189a3 View commit details
    Browse the repository at this point in the history
  11. Cut string allocations in content_tag_string

    content_tag's first argument is will generate a string with an html tag so `:a` will generate: `<a></a>`. When this happens, the symbol is implicitly `to_s`-d so a new string is allocated. We can get around that by using a frozen string instead which
    
    This change buys us 74,236 bytes of memory and 1,855 fewer objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    e76a843 View commit details
    Browse the repository at this point in the history
  12. Avoid calling to_s on nil in journey/formatter

    When `defaults[key]` in `generate` in the journey formatter is called, it often returns a `nil` when we call `to_s` on a nil, it allocates an empty string. We can skip this check when the default value is nil.
    
    This change buys us 35,431 bytes of memory and 887 fewer objects per request.
    
    Thanks to @matthewd for help with the readability
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    bff61ba View commit details
    Browse the repository at this point in the history
  13. Freeze a string in comparator

    Saves 888 string objects per request.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    045cdd3 View commit details
    Browse the repository at this point in the history
  14. Only allocate new string when needed

    Instead of calling `sub` on every link_to call for controller, we can detect when the string __needs__ to be allocated and only then create a new string (without the leading slash), otherwise, use the string that is given to us.
    
    Saves 888 string objects per request, 35,524 bytes.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    3fb9e80 View commit details
    Browse the repository at this point in the history
  15. Avoid hash duplication by skipping mutation

    If we don't mutate the `recall` hash, then there's no reason to duplicate it. While this change doesn't get rid of that many objects, each hash object it gets rid of was massive.
    
    Saves 888 string objects per request, 206,013 bytes (thats 0.2 mb which is kinda a lot).
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    1993e2c View commit details
    Browse the repository at this point in the history
  16. Decrease allocations in transliterate

    We can save a few objects by freezing the `replacement` string. We save a few more by down-casing the string in memory instead of allocating a new one. We save far more objects by checking for the default separator `"-"`, and using pre-generated regular expressions.
    
    We will save 209,231 bytes and 1,322 objects.
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    57ba9cb View commit details
    Browse the repository at this point in the history
  17. String#freeze optimizations

    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    0d7a714 View commit details
    Browse the repository at this point in the history
  18. Don't allocate array when not necessary

    In the `tag_options` method an array is used to build up elements, then `Array#*` (which is an alias for `Array#join` is called to turn the array into a string. Instead of allocating an array to build a string, we can build the string we want from the beginning.
    
    Saved: 121,743 bytes 893 objects
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    1f831fe View commit details
    Browse the repository at this point in the history
  19. zOMG 37 objects saved

    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    005541b View commit details
    Browse the repository at this point in the history
  20. Remove array allocation

    THe only reason we were allocating an array is to get the "missing_keys" variable in scope of the error message generator. Guess what? Arrays kinda take up a lot of memory, so by replacing that with a nil, we save:
    
    35,303 bytes and 886 objects per request
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    4d2ccc1 View commit details
    Browse the repository at this point in the history
  21. Remove (another) array allocation

    We don't always need an array when generating a url with the formatter. We can be lazy about allocating the `missing_keys` array. This saves us:
    
    35,606 bytes and 889 objects per request
    schneems committed Jul 30, 2015
    1 Configuration menu
    Copy the full SHA
    61dae88 View commit details
    Browse the repository at this point in the history
  22. Use delete_if instead of each; delete(key)

    It is slightly faster:
    
    ```
    Calculating -------------------------------------
            each; delete    35.166k i/100ms
               delete_if    36.416k i/100ms
    -------------------------------------------------
            each; delete    478.026k (± 8.5%) i/s -      2.391M
               delete_if    485.123k (± 7.9%) i/s -      2.440M
    ```
    schneems committed Jul 30, 2015
    Configuration menu
    Copy the full SHA
    22f5924 View commit details
    Browse the repository at this point in the history