미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
많은 장점에도 불구하고 Ruby on Rails는 여전히 상대적으로 느린 웹 프레임워크로 간주됩니다. 트위터가 Rails를 버리고 Scala를 선택했다는 것은 우리 모두 알고 있습니다. 하지만 몇 가지 개선 사항을 적용하면 앱을 훨씬 빠르게 실행할 수 있습니다!
Ruby 는 고도로 객체 지향적인 언어입니다. 사실, (거의) 모든 Ruby 는 객체입니다. 불필요한 객체를 만들면 프로그램에 많은 추가 메모리 사용량이 발생할 수 있으므로 이를 피해야 합니다.
차이를 측정하기 위해 메모리 프로파일러 겜과 내장된 벤치마크 모듈을 통해 시간 성능을 측정할 수 있습니다.
"memory_profiler" 필요
report = MemoryProfiler.report do
data = "X" * 1024 * 1024 * 100
데이터 = 데이터.소문자
end
report.pretty_print
아래 목록에서 100MB의 문자열을 생성하고 그 안에 포함된 각 문자를 대소문자로 구분했습니다. 벤치마크 결과 다음과 같은 보고서가 나옵니다:
총 할당량: 210765044 바이트(객체 6개)
그러나 6번 줄을 다음과 같이 바꾸면
데이터.다운케이스!
CSV 파일에서 2백만 개의 레코드로 구성된 방대한 데이터 모음을 가져와야 한다고 가정해 보겠습니다. 일반적으로 다음과 같은 형태가 될 것입니다:
'벤치마크' 필요
Benchmark.bm do |x|
x.report do
File.readlines("2mrecords.csv").map! {|line| line.split(",")}
end
end
사용자 시스템 총 실제
12.797000 2.437000 15.234000 (106.319865)
파일을 완전히 다운로드하는 데 106초가 넘게 걸렸습니다. 꽤나 긴 시간입니다! 하지만 이 프로세스의 속도를 높일 수 있습니다. 지도! 메서드를 사용하여 간단한 동안 루프:
'벤치마크' 필요
Benchmark.bm do |x|
x.report do
file = File.open("2mrecords.csv", "r")
while line = file.gets
line.split(",")
end
end
end
사용자 시스템 총 실제
6.078000 0.250000 6.328000 ( 6.649422)
이제 런타임이 크게 감소했습니다. 지도! 메서드는 다음과 같은 특정 클래스에 속합니다. Hash#map 또는 Array#map에서 Ruby 는 실행되는 동안 구문 분석된 파일의 모든 줄을 메모리 내에 저장합니다. 루비의 가비지 컬렉터 는 해당 이터레이터가 완전히 실행되기 전에는 메모리를 해제하지 않습니다. 그러나 한 줄씩 읽으면 GC를 통해 필요하지 않은 경우 이전 줄에서 메모리를 재배치합니다.
이것은 좀 더 일반적인 예를 들어 앞의 요점을 확장한 것입니다. 앞서 언급했듯이 Ruby 이터레이터는 객체 메서드이며 수행되는 동안에는 메모리를 해제하지 않습니다. 소규모에서는 그 차이가 무의미합니다(그리고 다음과 같은 메서드는 지도 가 더 읽기 쉬워 보입니다). 그러나 데이터 세트가 큰 경우에는 항상 더 기본적인 루프로 대체하는 것을 고려하는 것이 좋습니다. 아래 예시처럼 말입니다:
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
randoms.each do |line|
#한다
end
리팩토링 후
numberofelements = 10000000
randoms = Array.new(numberofelements) { rand(10) }
randoms.count > 0인 동안
line = randoms.shift
#한다
end
"`
이것은 빠르고 특히 유용한 팁입니다. 뒤에서 += 연산자를 사용하여 한 문자열을 다른 문자열에 추가하는 경우. Ruby 는 추가 객체를 생성합니다. 그래서 이것입니다:
a = "X"
b = "Y"
a += b
실제로는 이런 의미입니다:
a = "X"
b = "Y"
c = a + b
a = c
운영자는 이를 방지하여 메모리를 절약할 수 있습니다:
a = "X"
b = "Y"
a << b
그리고 레일즈 프레임워크 많은 "문제"를 최적화할 수 있는 코드 많은 추가 노력 없이 신속하게 처리할 수 있습니다.
포스트와 글쓴이라는 두 가지 관련 모델이 있다고 가정해 보겠습니다:
Author < ApplicationRecord 클래스
has_many :posts
end
클래스 Post < ApplicationRecord
belongs_to :작성자
end
컨트롤러에서 모든 게시물을 가져와 작성자와 함께 뷰에 렌더링하고 싶습니다:
컨트롤러
def index
@posts = Post.all.limit(20)
end
view
컨트롤러에서, 액티브 레코드 는 글을 찾기 위한 쿼리를 하나만 생성합니다. 하지만 나중에 각 작성자를 찾기 위해 20개의 쿼리가 추가로 트리거되어 시간이 더 걸리게 됩니다! 다행히도 Rails에는 이러한 쿼리를 하나의 쿼리로 결합하는 빠른 솔루션이 제공됩니다. 다음과 같이 포함 메서드를 사용하면 컨트롤러를 이런 식으로 다시 작성할 수 있습니다:
def index
@posts = Post.all.includes(:author).limit(20)
end
지금은 필요한 데이터만 하나의 쿼리로 가져옵니다.
다음과 같은 다른 보석도 사용할 수 있습니다. bullet 를 클릭하여 전체 프로세스를 사용자 지정할 수 있습니다.
액티브 레코드 속도를 높이는 또 다른 유용한 기술은 현재 목적에 필요한 속성만 호출하는 것입니다. 앱이 성장하기 시작하고 테이블당 열 수가 증가할 때 특히 유용합니다.
이전 코드를 예로 들어 작성자의 이름만 선택해야 한다고 가정해 보겠습니다. 따라서 컨트롤러를 다시 작성할 수 있습니다:
def index
@posts = Post.all.includes(:author).select("name").limit(20)
end
이제 컨트롤러에 필요한 속성을 제외한 모든 속성을 건너뛰도록 지시합니다.
이전 예제에서 게시물을 위한 별도의 파트를 만들려고 한다고 가정해 보겠습니다:
언뜻 보기에는 이 코드가 올바르게 보입니다. 그러나 렌더링할 게시물의 수가 많으면 전체 프로세스가 상당히 느려집니다. 그 이유는 다음과 같습니다. 레일 는 다시 한 번 새로운 반복으로 파트를 호출합니다. 이 문제를 해결하려면 컬렉션 기능:
<%= render @posts %>
지금, 레일 은 어떤 템플릿을 사용해야 하는지 자동으로 파악하여 한 번만 초기화합니다.
이메일 보내기, 통계 수집, 정기 보고서 제공 등 시간이 많이 걸리고 현재 흐름에 중요하지 않은 모든 프로세스는 백그라운드 처리의 좋은 후보로 고려할 수 있습니다.
사이드키크 은 백그라운드 처리에 가장 일반적으로 사용되는 보석입니다. 이 젬은 Redis 를 사용하여 작업을 저장할 수 있습니다. 또한 백그라운드 프로세스의 흐름을 제어하고, 별도의 대기열로 분할하고, 각 대기열별로 메모리 사용량을 관리할 수 있습니다.
레일 는 여러분의 삶을 더 쉽게 만들고 개발 프로세스를 가속화할 뿐만 아니라 애플리케이션의 성능 속도를 높여주는 수많은 보석들을 내놓았습니다. Devise 또는 Pundit과 같은 젬은 일반적으로 속도와 관련하여 충분한 테스트를 거쳤으며 동일한 목적을 위해 사용자 지정으로 작성된 코드보다 더 빠르고 안전하게 작동합니다.
개선할 사항이 있는 경우 레일즈 성능도달 범위 The Codest 엔지니어 를 통해 궁금한 점을 상담하세요.
자세히 읽어보세요: