Trots sina många fördelar anses Ruby on Rails fortfarande vara ett relativt långsamt webbramverk. Vi vet alla att Twitter har lämnat Rails till förmån för Scala. Men med några smarta förbättringar kan du köra din app betydligt snabbare!
Ruby First
Ruby är ett starkt objektorienterat språk. Faktum är att (nästan) allt i Ruby är ett objekt. Att skapa onödiga objekt kan kosta ditt program en hel del extra minnesanvändning, så du måste undvika det.
För att mäta skillnaden kommer vi att använda en minne_profilerare gem och en inbyggd Benchmark-modul för att mäta tidsprestanda.
Använd bang!-metoder på strängar
kräver "memory_profiler"
rapport = MemoryProfiler.rapport do
data = "X" * 1024 * 1024 * 100
data = data.downcase
slut
rapport.pretty_print
I listan nedan skapade vi en 100 MB-sträng och minskade varje tecken som fanns i den. Vårt riktmärke ger oss följande rapport:
Totalt allokerad: 210765044 byte (6 objekt)
Men om vi ersätter rad 6 med:
data.downcase!
Läs filer rad för rad
Förmodligen måste vi hämta en enorm datainsamling med 2 miljoner poster från en csv-fil. Vanligtvis skulle det se ut så här:
kräver "benchmark
Benchmark.bm do |x|
x.rapport do
File.readlines("2mrecords.csv").map! {|linje| linje.split(",")}
slut
slut
användare system totalt verklig
12.797000 2.437000 15.234000 (106.319865)
Det tog oss mer än 106 sekunder att ladda ner filen helt och hållet. Ganska mycket! Men vi kan påskynda processen genom att byta ut karta! metod med en enkel medan loop:
kräver "benchmark
Benchmark.bm do |x|
x.rapport do
fil = File.open("2mrecords.csv", "r")
while rad = fil.får
rad.split(",")
slut
slut
slut
användare system totalt verklig
6.078000 0.250000 6.328000 ( 6.649422)
Drifttiden har nu sjunkit drastiskt sedan karta! metod tillhör en specifik klass, till exempel Hash#map eller Array#map, där Ruby kommer att lagra varje rad i den analyserade filen i minnet så länge den körs. Rubys skräpsamlare kommer inte att frigöra minnet innan dessa iteratorer är helt exekverade. Men om du läser den rad för rad kommer GC att flytta minnet från de tidigare raderna när det inte är nödvändigt.
Undvik metoditeratorer på större samlingar
Den här punkten är en förlängning av den föregående punkten med ett vanligare exempel. Som jag nämnde tidigare, Ruby iteratorer är objektmetoder och de kommer inte att frigöra minnet så länge de utförs. På en liten skala är skillnaden meningslös (och metoder som karta verkar mer läsbar). Men när det gäller större datamängder är det alltid en bra idé att överväga att ersätta den med mer grundläggande loopar. Som i exemplet nedan:
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
randoms.each do |linje|
#gör något
slut
och efter refaktorisering:
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
while randoms.count > 0
linje = randoms.skift
#gör något
slut
"`
Använd String::<<-metoden
Detta är ett snabbt men ändå särskilt användbart tips. Om du lägger till en sträng till en annan med hjälp av +=-operatorn bakom kulisserna. Ruby kommer att skapa ytterligare ett objekt. Så, det här:
a = "X"
b = "Y"
a += b
Egentligen betyder det här:
a = "X"
b = "Y"
c = a + b
a = c
Operatören skulle undvika det och spara dig lite minne:
a = "X"
b = "Y"
a << b
Låt oss prata om Rails
Den Rails ramverk har gott om "gotchas" som gör det möjligt för dig att optimera din kod snabbt och utan alltför mycket extra ansträngning.
Eager Loading AKA n+1 frågeställningsproblem
Låt oss anta att vi har två associerade modeller, Post och Author:
class Författare < ApplicationRecord
has_many :inlägg
slut
klass Post < ApplikationsRecord
tillhör :author
slut
Vi vill hämta alla inlägg i vår controller och rendera dem i en vy med deras författare:
Styrenhet
def index
@posts = Post.all.limit(20)
slut
vy
I controllern, ActiveRecord skapar bara en fråga för att hitta våra inlägg. Men senare kommer det också att utlösa ytterligare 20 frågor för att hitta varje författare i enlighet därmed - vilket tar upp ytterligare tid! Lyckligtvis kommer Rails med en snabb lösning för att kombinera dessa frågor till en enda. Genom att använda inkluderar metoden kan vi skriva om vår controller på detta sätt:
def index
@posts = Post.all.includes(:författare).limit(20)
slut
För närvarande kommer endast nödvändig data att hämtas till en fråga.
Du kan också använda andra ädelstenar, t.ex. kula för att skräddarsy hela processen.
Ring bara det du behöver
En annan användbar teknik för att öka ActiveRecord-hastigheten är att bara ringa de attribut som är nödvändiga för dina nuvarande ändamål. Detta är särskilt användbart när din app börjar växa och antalet kolumner per tabell också ökar.
Låt oss ta vår tidigare kod som ett exempel och anta att vi bara behöver välja namn från författare. Så vi kan skriva om vår controller:
def index
@posts = Post.all.includes(:author).select("name").limit(20)
slut
Nu instruerar vi vår controller att hoppa över alla attribut utom det vi behöver.
Rendera partialer på rätt sätt
Låt oss säga att vi vill skapa en separat partial för våra inlägg från tidigare exempel:
Vid första anblicken ser den här koden korrekt ut. Men med ett större antal inlägg att rendera kommer hela processen att bli betydligt långsammare. Detta beror på att Räls anropar vår partial med en ny iteration en gång till. Vi kan åtgärda det genom att använda samlingar funktion:
Nu, Räls kommer automatiskt att räkna ut vilken mall som ska användas och initiera den bara en gång.
Använd bakgrundsbehandling
Varje process som är mer tidskrävande och inte avgörande för ditt nuvarande flöde kan anses vara en bra kandidat för bakgrundsbehandling, t.ex. att skicka e-post, samla in statistik eller tillhandahålla periodiska rapporter.
Sidekiq är den vanligaste pärlan för bakgrundsbehandling. Den använder Redis för att lagra uppgifter. Du kan också styra flödet i dina bakgrundsprocesser, dela upp dem i separata köer och hantera minnesanvändningen för var och en av dem.
Skriv mindre kod, använd fler ädelstenar
Räls har ett enormt antal gems som inte bara gör livet enklare och påskyndar utvecklingsprocessen, utan också ökar prestandahastigheten i din applikation. Gems som Devise eller Pundit är vanligtvis väl testade när det gäller deras hastighet och fungerar snabbare och säkrare än kod som är specialskriven för samma ändamål.
Vid eventuella frågor till förbättring Rails prestanda, räckvidd Codest-ingenjörerna ut för att rådgöra med dina tvivel.