Created
August 24, 2020 16:22
-
-
Save jrochkind/764fb0985b7af3c8d9252e33d47d713e to your computer and use it in GitHub Desktop.
Comparing S3 public_url generation implementations for performance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A local domain ActiveRecord, Asset#file is a shrine attachment | |
asset = Asset.last | |
asset.file # load outside of benchmarking | |
s3_object = asset.file.storage.object(asset.file.id) # Aws::S3::Object | |
require 'erb' | |
require 'escape_utils' | |
def naive_public_url(shrine_file) | |
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}" | |
end | |
def naive_with_escaping(shrine_file) | |
path = shrine_file.id.gsub(/[^\/]+/) { |s| ERB::Util.url_encode(s) } | |
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}" | |
end | |
# escape_utils looks unmaintained, but has a faster C implementation... | |
# | |
# https://github.com/brianmario/escape_utils | |
# https://rubygems.org/gems/escape_utils/ | |
# | |
# Note this doesn't escape the same as original implementation... but original implementation over-escapes! | |
def naive_with_escape_utils_escaping(shrine_file) | |
# because EscapeUtils.escape_uri does NOT escape `/`, we don't need to split it, which is what | |
# actually saves us the time. | |
path = EscapeUtils.escape_uri(shrine_file.id) | |
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}" | |
end | |
# URI.escape may do exactly what we need without requiring splitting on "/", but | |
# it's sadly deprecated when it really ought not to be | |
def naive_with_uri_escape_escaping(shrine_file) | |
# because URI.escape does NOT escape `/`, we don't need to split it, which is what | |
# actually saves us the time. | |
path = URI.escape(shrine_file.id) | |
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}" | |
end | |
class Aws::S3::Object | |
def faster_public_url(options = {}) | |
url = bucket.url(options) | |
url << "/" unless url.end_with?('/') | |
url << key.gsub(/[^\/]+/) { |s| ERB::Util.url_encode(s) } | |
end | |
end | |
ITERATIONS = (ENV['ITERATIONS'] || "1200").to_i | |
puts "Iterations: #{ITERATIONS}" | |
Benchmark.bmbm do |x| | |
x.report("original AWS SDK public_url implementation") do | |
ITERATIONS.times do | |
s3_object.public_url | |
end | |
end | |
x.report("optimized implementation") do | |
ITERATIONS.times do | |
s3_object.faster_public_url | |
end | |
end | |
x.report("naive implementation") do | |
ITERATIONS.times do | |
naive_public_url(asset.file) | |
end | |
end | |
x.report("naive with escaping") do | |
ITERATIONS.times do | |
naive_with_escaping(asset.file) | |
end | |
end | |
x.report("naive with escape_utils escaping") do | |
ITERATIONS.times do | |
naive_with_escape_utils_escaping(asset.file) | |
end | |
end | |
x.report("naive with URI.escape escaping") do | |
ITERATIONS.times do | |
naive_with_uri_escape_escaping(asset.file) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment