Skip to content

Instantly share code, notes, and snippets.

@alekseyl
Last active January 12, 2025 19:58
Show Gist options
  • Save alekseyl/5d08782808a29df6813f16965f70228a to your computer and use it in GitHub Desktop.
Save alekseyl/5d08782808a29df6813f16965f70228a to your computer and use it in GitHub Desktop.
nested_select_profiling
# This is a simple gist for performance and RAM usage testing for a nested_select gem
#
# Objects relations: course -> topics -> lessons ( all relations are has_many )
#
# -------------------------------------------------------------------------
# Schemas:
#
# create_table "lessons", force: :cascade do |t|
# t.string "title", null: false, comment: "Тема урока"
# t.string "description", null: false, comment: "Описание урока"
# t.integer "topic_id", null: false
# t.datetime "created_at", precision: 6, null: false
# t.datetime "updated_at", precision: 6, null: false
# t.integer "partition", default: 1, null: false
# t.integer "position", default: 0
# t.integer "parent_id", default: 0
# t.integer "education_year"
# t.text "text"
# t.boolean "actived", default: true
# t.boolean "is_public", default: false
# t.boolean "online", default: false
# t.datetime "deleted_at"
# t.integer "kind"
# t.string "recording_link"
# t.jsonb "body"
# t.datetime "content_updated_at"
# t.index ["content_updated_at"], name: "index_lessons_on_content_updated_at", where: "(content_updated_at IS NOT NULL)"
# t.index ["topic_id"], name: "index_lessons_on_topic_id"
# end
#
# create_table "topics", force: :cascade do |t|
# t.string "title", null: false, comment: "Тема модуля"
# t.string "description", null: false, comment: "Описание модуля"
# t.integer "course_id", null: false
# t.datetime "created_at", precision: 6, null: false
# t.datetime "updated_at", precision: 6, null: false
# t.integer "partition", default: 1, null: false
# t.integer "position", default: 0
# t.boolean "free"
# t.datetime "deleted_at"
# t.integer "kind"
# t.integer "lessons_count", default: 0, null: false
# t.index "to_tsvector('russian'::regconfig, (title)::text)", name: "gin_index_topics_on_title", using: :gin
# t.index ["course_id"], name: "index_topics_on_course_id"
# end
#
# -------------------------------------------------------------------------
#
# Rem: Outside of this method I have preselected a set of course ids, and providing them via root_ids attribute
#
require 'nested_select'
def compare_nested_select( root_ids, root_collection_size, silence_ar_logger_for_memory_profiling: true)
puts "\n------- CPU comparison, for root_collection_size: #{root_collection_size} ----"
ActiveRecord::Base.logger.silence do
Benchmark.bm do |benchmark|
benchmark.report("nested_select") do
50.times do
Topic.where(course_id: root_ids.sample(root_collection_size)).includes(:lessons)
.select(:id, :position, :title, :course_id, lessons: [:id, :title, :topic_id, :position]).load
end
end
benchmark.report("simple includes") do
50.times do
Topic.where(course_id: root_ids.sample(root_collection_size)).includes(:lessons)
.select(:id, :position, :title, :course_id,).load
end
end
end
end
puts "\n----------------- Memory comparison, for root_collection_size: #{root_collection_size} ---------"
ids_sample = root_ids.sample(root_collection_size) # it should be the same id for both tests, since we comparing memory consumption
ActiveRecord::Base.logger.level = :error if silence_ar_logger_for_memory_profiling
ns = MemoryProfiler.report do
Topic.where(course_id: ids_sample)
.includes(:lessons)
.select(:id, :position, :title, :course_id, lessons: [:id, :title, :topic_id, :position]).load
end
direct_include = MemoryProfiler.report do
Topic.where(course_id: ids_sample).select(:id, :position, :title, :course_id,).includes(:lessons).load
end
ActiveRecord::Base.logger.level = :debug if silence_ar_logger_for_memory_profiling
puts "------ Nested Select memory consumption for root_collection_size: #{root_collection_size} ------"
ns.pretty_print(detailed_report: false, scale_bytes: true)
puts "------ Full preloading memory consumption for root_collection_size: #{root_collection_size} ----"
direct_include.pretty_print(detailed_report: false, scale_bytes: true)
puts "RAM ratio improvements x#{direct_include.total_retained_memsize.to_f / ns.total_retained_memsize} on retain objects"
puts "RAM ratio improvements x#{direct_include.total_allocated_memsize.to_f / ns.total_allocated_memsize} on total_allocated objects"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment