Trotz seiner zahlreichen Vorteile gilt Ruby on Rails immer noch als ein relativ langsames Web-Framework. Wir alle wissen, dass Twitter Rails zu Gunsten von Scala verlassen hat. Mit ein paar cleveren Verbesserungen können Sie Ihre App jedoch deutlich schneller ausführen!
Rubinrot zuerst
Rubinrot ist eine stark objektorientierte Sprache. In der Tat ist (fast) alles in Rubinrot ist ein Objekt. Die Erstellung unnötiger Objekte kann Ihr Programm viel zusätzlichen Speicherplatz kosten, weshalb Sie dies vermeiden sollten.
Um den Unterschied zu messen, werden wir eine speicher_profiler gem und ein eingebautes Benchmark-Modul zur Messung der Zeitleistung.
Verwenden Sie bang! Methoden auf Strings
erfordern "memory_profiler"
report = MemoryProfiler.report do
Daten = "X" * 1024 * 1024 * 100
data = data.downcase
end
report.pretty_print
In der folgenden Auflistung haben wir eine 100 MB große Zeichenkette erstellt und jedes darin enthaltene Zeichen heruntergeschrieben. Unser Benchmark liefert uns den folgenden Bericht:
Insgesamt zugewiesen: 210765044 bytes (6 Objekte)
Ersetzen wir jedoch Zeile 6 durch:
Daten.downcase!
Dateien zeilenweise lesen
Angenommen, wir müssen eine große Datensammlung von 2 Millionen Datensätzen aus einer CSV-Datei abrufen. Normalerweise würde das so aussehen:
erfordern 'Benchmark'
Benchmark.bm do |x|
x.report do
File.readlines("2mrecords.csv").map! {|line| line.split(",")}
end
end
Benutzer System insgesamt real
12.797000 2.437000 15.234000 (106.319865)
Wir haben mehr als 106 Sekunden gebraucht, um die Datei vollständig herunterzuladen. Das ist eine ganze Menge! Aber wir können diesen Prozess beschleunigen, indem wir die Karte! Methode mit einer einfachen während Schleife:
erfordern 'Benchmark'
Benchmark.bm do |x|
x.report do
file = File.open("2mrecords.csv", "r")
while Zeile = file.gets
line.split(",")
end
end
end
Benutzer System insgesamt real
6.078000 0.250000 6.328000 ( 6.649422)
Die Laufzeit hat sich nun drastisch verringert, da die Karte! Methode gehört zu einer bestimmten Klasse, wie Hash#map oder Array#map, wobei Rubinrot speichert jede Zeile der geparsten Datei im Speicher, solange sie ausgeführt wird. Rubys Garbage Collector gibt den Speicher nicht frei, bevor diese Iteratoren vollständig ausgeführt wurden. Beim zeilenweisen Lesen wird jedoch der Speicher aus den vorherigen Zeilen ausgelagert, wenn dies nicht erforderlich ist.
Vermeiden Sie Methoden-Iteratoren bei größeren Sammlungen
Dieser Punkt ist eine Erweiterung des vorherigen Punktes mit einem häufigeren Beispiel. Wie ich bereits erwähnt habe, Rubinrot Iteratoren sind Objektmethoden, und sie geben den Speicher nicht frei, solange sie ausgeführt werden. Auf einer kleinen Skala ist der Unterschied bedeutungslos (und Methoden wie Karte scheint besser lesbar zu sein). Bei größeren Datenmengen ist es jedoch immer eine gute Idee, sie durch einfachere Schleifen zu ersetzen. Wie im folgenden Beispiel:
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
randoms.each do |line|
#etwas tun
end
und nach dem Refactoring:
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
while randoms.count > 0
Zeile = randoms.shift
#etwas tun
end
"`
Methode String::<< verwenden
Dies ist ein schneller, aber besonders nützlicher Tipp. Wenn Sie hinter den Kulissen eine Zeichenkette an eine andere anhängen, indem Sie den Operator += verwenden. Rubinrot wird ein zusätzliches Objekt erstellt. Also, dies:
a = "X"
b = "Y"
a += b
Das bedeutet eigentlich Folgendes:
a = "X"
b = "Y"
c = a + b
a = c
Der Operator würde das vermeiden und Ihnen etwas Speicherplatz sparen:
a = "X"
b = "Y"
a << b
Lassen Sie uns über Rails sprechen
Die Rails-Framework verfügt über eine Fülle von "gotchas", die es Ihnen ermöglichen würde, Ihre Code schnell und ohne allzu großen zusätzlichen Aufwand.
Eager Loading AKA n+1 Abfrageproblem
Nehmen wir an, wir haben zwei verbundene Modelle, Post und Author:
Klasse Author < ApplicationRecord
has_many :posts
end
Klasse Post < ApplicationRecord
gehört_zu :Autor
end
Wir möchten alle Beiträge in unserem Controller abrufen und sie in einer Ansicht mit ihren Autoren darstellen:
Controller
def index
@posts = Post.all.limit(20)
end
view
Im Controller, ActiveRecord wird nur eine Abfrage erstellt, um unsere Beiträge zu finden. Später werden jedoch 20 weitere Abfragen ausgelöst, um jeden einzelnen Autor zu finden, was zusätzliche Zeit in Anspruch nimmt! Glücklicherweise bietet Rails eine schnelle Lösung, um diese Abfragen in einer einzigen zu kombinieren. Durch die Verwendung der enthält Methode können wir unseren Controller folgendermaßen umschreiben:
def index
@posts = Post.all.includes(:author).limit(20)
end
Vorerst werden nur die erforderlichen Daten in einer Abfrage abgefragt.
Sie können auch andere Edelsteine verwenden, wie z. B. Aufzählung um den gesamten Prozess individuell zu gestalten.
Rufen Sie nur an, was Sie brauchen
Eine weitere nützliche Technik, um die Geschwindigkeit von ActiveRecord zu erhöhen, besteht darin, nur die Attribute aufzurufen, die für Ihre aktuellen Zwecke erforderlich sind. Dies ist besonders nützlich, wenn Ihre Anwendung zu wachsen beginnt und die Anzahl der Spalten pro Tabelle ebenfalls zunimmt.
Nehmen wir unseren vorherigen Code als Beispiel und gehen wir davon aus, dass wir nur Namen von Autoren auswählen müssen. Wir können also unseren Controller umschreiben:
def index
@posts = Post.all.includes(:author).select("name").limit(20)
end
Jetzt weisen wir unseren Controller an, alle Attribute zu überspringen, außer dem, das wir brauchen.
Richtiges Rendern von Partials
Nehmen wir an, wir wollen einen separaten Teilbereich für unsere Beiträge aus den vorherigen Beispielen erstellen:
Auf den ersten Blick sieht dieser Code korrekt aus. Bei einer größeren Anzahl von Beiträgen, die gerendert werden müssen, wird der gesamte Prozess jedoch deutlich langsamer. Der Grund dafür ist Schiene ruft unser Teilprogramm mit einer neuen Iteration erneut auf. Wir können das beheben, indem wir die Sammlungen Funktion:
Jetzt, Schiene findet automatisch heraus, welche Vorlage verwendet werden soll und initialisiert sie nur einmal.
Hintergrundverarbeitung verwenden
Jeder Prozess, der mehr Zeit in Anspruch nimmt und für Ihren aktuellen Arbeitsablauf nicht entscheidend ist, könnte ein guter Kandidat für die Hintergrundverarbeitung sein, z. B. das Versenden von E-Mails, das Sammeln von Statistiken oder das Erstellen regelmäßiger Berichte.
Sidekiq ist der am häufigsten verwendete Edelstein für die Hintergrundverarbeitung. Es verwendet Redis um Aufgaben zu speichern. Außerdem können Sie den Ablauf Ihrer Hintergrundprozesse steuern, sie in separate Warteschlangen aufteilen und die Speichernutzung für jeden einzelnen Prozess verwalten.
Weniger Code schreiben, mehr Edelsteine verwenden
Schiene hat eine enorme Anzahl von Gems entwickelt, die Ihnen nicht nur das Leben leichter machen und den Entwicklungsprozess beschleunigen, sondern auch die Geschwindigkeit Ihrer Anwendung erhöhen. Gems wie Devise oder Pundit sind in der Regel hinsichtlich ihrer Geschwindigkeit gut getestet und arbeiten schneller und sicherer als Code, der für denselben Zweck geschrieben wurde.
Bei Fragen zur Verbesserung Leistung von Rails, erreichen Die Codest-Ingenieure um Ihre Zweifel zu äußern.