O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

Ruby memory tips and tricks

A talk I gave at the Perth Ruby on Rails meetup group in December 2017 with a beginners level guide to garbage collection and memory tips and tricks

  • Entre para ver os comentários

Ruby memory tips and tricks

  1. 1. Ruby Memory Tips and Tricks
  2. 2. Hi, I’m Bruce 6 ½ years experience developing complex Ruby on Rails applications 4 years as Senior Lead Developer at Prezentt 5 years as Technical Director at GTP iCommerce
  3. 3. Agenda • Introduction to the Garbage Collector • Garbage Collector Tuning • General tips and tricks
  4. 4. C code vs Ruby cde void my_function() { char *stack = "Hello"; char *heap = malloc(6); strncopy(heap, "world", 5); free(heap); } Memory explicitly allocated on the stack or the heap Heap allocated memory must be free()’d or it will leak def my_function local = "Hello" @instance = "world" end No stack allocated variables, even local variables live on the heap Memory is freed automatically How does it do this?
  5. 5. Garbage Collection The Ruby interpreter will trigger garbage collection to free memory when certain conditions are met. Ruby uses a “Stop the world” mechanism which is the cause of many performance problems.
  6. 6. How Garbage Collection Works Ruby < 2.0 used various mark and sweep algorithms. Traverses object graph and checks if the memory is still in use. Ruby 2.0 replaced it with a Bitmap Marking algorithm. Each heap has a corresponding memory structure with bit values indicating if allocated memory is still in use. Ruby 2.1 added a Generational Garbage Collection algorithm. “Minor” sweep focuses on newly allocated memory. “Major” sweep checks entire object graph.
  7. 7. Garbage Collection Triggers Minor Garbage Collection (Fast, looks at new objects) • Not enough space on the heap to allocate new objects • Every 16-32Mb of memory allocated in new objects Major Garbage Collection (Slow, looks at all objects) • Number of old objects increases by more than a factor of 2 • Every 16-32Mb of memory allocated in old objects
  8. 8. Garbage Collection Tuning Goal is to manage a tradeoff between memory usage vs. the frequency and duration of garbage collection. RUBY_GC_HEAP_GROWTH_FACTOR (1.8) The factor by which the size of the heap grows when it needs to be expanded RUBY_GC_MALLOC_LIMIT & _MAX (16Mb - 32Mb) The minimum value for malloc limits that would trigger a Minor Garbage Collection. RUBY_GC_OLDMALLOC_LIMIT & _MAX (16Mb - 32Mb) The minimum value for malloc limits that would trigger a Major Garbage Collection.
  9. 9. Garbage Collection Tuning (Cont) How do we know which values to change, and to what? • Monitor changes with GC.stat • Great third party tools like New Relic and Scout • My favourite: TuneMyGC (https://tunemygc.com/)
  10. 10. TuneMyGC How do we know which values t change, and to what? • Monitor changes with GC.stat • Great third party tools like New Relic and Scout • My favourite: TuneMyGC (https://tunemygc.com/)
  11. 11. TuneMyGC
  12. 12. TuneMyGC
  13. 13. TuneMyGC
  14. 14. Ruby Memory Tools allocation_tracer gem memory_profiler gem Third party tools like New Relic and Scout
  15. 15. Memory Efficient Ruby Lots of advice out there on micro-optimisation, avoid: require 'objspace' ObjectSpace.memsize_of([]) # => 40 ObjectSpace.memsize_of([1,2,3]) # => 40 ObjectSpace.memsize_of([1,2,3,4]) # => 72 Writing beautiful Ruby is more important than saving mere bytes. The best memory saving comes from not allocating additional objects if you can avoid it.
  16. 16. Memory Efficient Ruby require 'memory_profiler' large_array = [*1..1_000_000] report = MemoryProfiler.report do new_array = large_array.map { |el| el * 2 } end report.total_allocated_memsize # => 8000040 report = MemoryProfiler.report do large_array.map! { |el| el * 2 } end report.total_allocated_memsize # => 0
  17. 17. Memory Efficient Ruby require 'memory_profiler' string_1 = "Hello" string_2 = "world" string_1.object_id # => 47213217995200 report = MemoryProfiler.report do string_1 << string_2 end report.total_allocated_memsize # => 0 string_1.object_id # => 47213217995200 report = MemoryProfiler.report do string_1 += string_2 end report.total_allocated_memsize # => 40 string_1.object_id # => 47213218585840
  18. 18. Memory Efficient Ruby # I want to turn [1, 2, 3] into {1 => 1, 2 => 2, 3 => 3} require 'memory_profiler' require 'benchmark' large_array = [*1..15_000] def merge!(array) array.inject({}) { |k, v| k.merge!(v => v) } end def merge(array) array.inject({}) { |k, v| k.merge(v => v) } end report = MemoryProfiler.report do merge(large_array) end report.total_allocated_memsize # => 4_380_180_392 report = MemoryProfiler.report do merge!(large_array) end report.total_allocated_memsize # => 3_338_848
  19. 19. Memory Efficient Ruby # I want to turn [1, 2, 3] into {1 => 1, 2 => 2, 3 => 3} require 'memory_profiler' require 'benchmark' large_array = [*1..15_000] def merge!(array) array.inject({}) { |k, v| k.merge!(v => v) } end def merge(array) array.inject({}) { |k, v| k.merge(v => v) } end Benchmark.bm(10) do |b| b.report("merge") { merge(large_array) } b.report("merge!") { merge!(large_array) } end # user system total real # merge 7.390000 0.080000 7.470000 ( 7.475560) # merge! 0.010000 0.000000 0.010000 ( 0.008071)
  20. 20. Thanks! Can be reached at: bruce@werdschinski.com @bwerdschinski http://www.werdschinski.com

×