Til tross for sine mange fordeler anses Ruby on Rails fortsatt for å være et relativt tregt webrammeverk. Vi vet alle at Twitter har forlatt Rails til fordel for Scala. Men med noen få smarte forbedringer kan du kjøre appen din betydelig raskere!
Ruby First
Ruby er et sterkt objektorientert språk. Faktisk er (nesten) alt i Ruby er et objekt. Hvis du oppretter unødvendige objekter, kan det koste programmet ditt mye ekstra minnebruk, så du må unngå det.
For å måle forskjellen bruker vi en minne_profiler gem og en innebygd Benchmark-modul for å måle tidsytelsen.
Bruk bang!-metoder på strenger
krever "memory_profiler"
report = MemoryProfiler.report do
data = "X" * 1024 * 1024 * 100
data = data.downcase
end
rapport.pretty_print
I listen nedenfor har vi opprettet en streng på 100 MB og nedkortet hvert tegn i den. Vår benchmark gir oss følgende rapport:
Totalt tildelt: 210765044 byte (6 objekter)
Men hvis vi erstatter linje 6 med:
data.downcase!
Lese filer linje for linje
Vi antar at vi trenger å hente en stor datasamling med 2 millioner poster fra en csv-fil. Vanligvis vil det se slik ut:
krever "benchmark
Benchmark.bm do |x|
x.rapport do
File.readlines("2mrecords.csv").map! {|linje| linje.split(",")}
end
end
bruker system total real
12.797000 2.437000 15.234000 (106.319865)
Det tok oss mer enn 106 sekunder å laste ned hele filen. Ganske mye! Men vi kan gjøre denne prosessen raskere ved å bytte ut Kart! metode med en enkel mens loop:
krever 'benchmark'
Benchmark.bm do |x|
x.rapport do
file = File.open("2mrecords.csv", "r")
while line = file.gets
line.split(",")
end
end
end
bruker system total real
6.078000 0.250000 6.328000 ( 6.649422)
Kjøretiden har nå sunket drastisk siden Kart! metoden tilhører en bestemt klasse, som Hash#map eller Array#map, hvor Ruby vil lagre hver linje i den analyserte filen i minnet så lenge den kjøres. Rubys søppelsamler vil ikke frigjøre minnet før disse iteratorene er ferdig kjørt. Hvis du leser den linje for linje, vil GC imidlertid flytte minnet fra de foregående linjene når det ikke er nødvendig.
Unngå metodeiteratorer på større samlinger
Dette er en forlengelse av det forrige punktet med et mer vanlig eksempel. Som jeg nevnte tidligere, Ruby iteratorer er objektmetoder, og de frigjør ikke minnet så lenge de utføres. I liten skala er forskjellen meningsløs (og metoder som kart virker mer leselig). Når det gjelder større datasett, er det imidlertid alltid en god idé å vurdere å erstatte den med mer grunnleggende sløyfer. Som i eksempelet nedenfor:
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
randoms.each do |line|
#gjør noe
end
og etter refaktorisering:
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
while randoms.count > 0
linje = randoms.shift
#gjør noe
slutt
"`
Bruk String::<<-metoden
Dette er et raskt, men særdeles nyttig tips. Hvis du legger til en streng til en annen ved hjelp av +=-operatoren bak kulissene. Ruby vil opprette et ekstra objekt. Så, dette:
a = "X"
b = "Y"
a += b
Det betyr faktisk dette:
a = "X"
b = "Y"
c = a + b
a = c
Operatøren vil unngå det, og spare deg for litt minne:
a = "X"
b = "Y"
a << b
La oss snakke om Rails
Den Rails-rammeverket har massevis av "problemer" som lar deg optimalisere din kode raskt og uten for mye ekstra innsats.
Ivrig lasting, også kjent som n+1-spørringsproblemet
La oss anta at vi har to tilknyttede modeller, Post og Author:
class Forfatter < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :author
end
Vi ønsker å hente alle innleggene i kontrolleren vår og gjengi dem i en visning med forfatterne:
kontrollør
def index
@posts = Post.all.limit(20)
end
visning
I kontrolleren, ActiveRecord vil bare opprette én spørring for å finne innleggene våre. Men senere vil det også utløse ytterligere 20 spørringer for å finne hver forfatter tilsvarende - noe som tar ekstra tid! Heldigvis kommer Rails med en rask løsning for å kombinere disse spørringene til en enkelt. Ved å bruke inkluderer metoden, kan vi omskrive kontrolleren vår på denne måten:
def index
@posts = Post.all.includes(:author).limit(20)
end
Inntil videre vil bare de nødvendige dataene bli hentet inn i én spørring.
Du kan også bruke andre edelstener, for eksempel kule for å tilpasse hele prosessen.
Ring bare det du trenger
En annen nyttig teknikk for å øke hastigheten i ActiveRecord er å bare kalle opp de attributtene som er nødvendige for det aktuelle formålet. Dette er spesielt nyttig når appen begynner å vokse og antallet kolonner per tabell øker.
La oss ta den forrige koden som et eksempel og anta at vi bare trenger å velge navn fra forfattere. Da kan vi skrive om kontrolleren vår:
def index
@posts = Post.all.includes(:author).select("name").limit(20)
end
Nå instruerer vi kontrolleren vår om å hoppe over alle attributter unntatt det vi trenger.
Gjengi partialer på riktig måte
La oss si at vi ønsker å opprette en egen partiell for innleggene våre fra tidligere eksempler:
Ved første øyekast ser denne koden riktig ut. Men med et større antall innlegg som skal gjengis, vil hele prosessen gå betydelig saktere. Dette skyldes at Rails påkaller partialen vår med en ny iterasjon igjen. Vi kan fikse det ved å bruke samlinger funksjon:
Nå, Rails vil automatisk finne ut hvilken mal som skal brukes, og initialiserer den bare én gang.
Bruk bakgrunnsbehandling
Alle prosesser som er mer tidkrevende og ikke er avgjørende for den nåværende flyten, kan være gode kandidater for bakgrunnsbehandling, for eksempel sending av e-post, innhenting av statistikk eller periodiske rapporter.
Sidekiq er den mest brukte perlen for bakgrunnsbehandling. Den bruker Redis for å lagre oppgaver. Du kan også kontrollere flyten i bakgrunnsprosessene dine, dele dem inn i separate køer og administrere minnebruken for hver enkelt av dem.
Skriv mindre kode, bruk flere edelstener
Rails har utviklet et enormt antall gems som ikke bare gjør livet ditt enklere og fremskynder utviklingsprosessen, men som også øker ytelseshastigheten til applikasjonen din. Gems som Devise eller Pundit er vanligvis godt testet med hensyn til hastighet og fungerer raskere og sikrere enn kode som er spesialskrevet for samme formål.
I tilfelle spørsmål til forbedring Rails-ytelse, rekkevidde Codest-ingeniørene ut for å konsultere din tvil.