Skip to content

Autoload Resolv instead of requiring it eagerly - #337

Open
tas50 wants to merge 1 commit into
ruby:masterfrom
tas50:perf/defer-resolv-require
Open

Autoload Resolv instead of requiring it eagerly#337
tas50 wants to merge 1 commit into
ruby:masterfrom
tas50:perf/defer-resolv-require

Conversation

@tas50

@tas50 tas50 commented Aug 28, 2026

Copy link
Copy Markdown

Problem

net/http requires resolv at the top of the file, but the only thing it uses from that library is two regexp constants, on a single line:

# Server Name Indication (SNI) RFC 3546/6066
case @address
when Resolv::IPv4::Regex, Resolv::IPv6::Regex
  # don't set SNI, as IP addresses in SNI is not valid

That line lives in the TLS branch of #connect, so resolv isn't needed until an HTTPS connection is opened — and not at all for plain HTTP, or for the many libraries that require 'net/http' to expose optional functionality and never connect.

   81.54  net/http
   59.10    net/protocol
   54.09      socket
    9.78    uri
    6.33    resolv        <-- for two regexps
    0.77    zlib

Fix

-require 'resolv'
 autoload :OpenSSL, 'openssl'
+autoload :Resolv, 'resolv'

autoload rather than a require inside #connect, for two reasons:

  1. It matches what the very next line of this file already does for OpenSSL, so it's the file's existing idiom rather than a new pattern.
  2. It's backward compatible. Resolv stays resolvable, so anything downstream that relied on require 'net/http' defining it keeps working — the load just happens on first reference instead of eagerly. An inline require would have removed the constant from the post-require namespace.

It also costs nothing per connection, which an inline require in connect would not.

Measurements

Ruby 4.0.6 (arm64-darwin), best of seven runs:

require 'net/http' files loaded
before 76.94 ms 33
after 70.54 ms 30
−6.40 ms (8%) −3

Verified directly:

require 'net/http'          -> resolv loaded? NOT loaded
Resolv::IPv4::Regex         -> Regexp, resolv now loaded? true
Net::HTTP.get_response(URI("https://example.com/")) -> 200

Tests

bundle exec rake test: 201 tests, 0 failures, unchanged (204 with the additions).

The three added tests cover that requiring net/http does not load resolv, that Resolv::IPv4::Regex still resolves afterwards, and that referencing it pulls the library in. The first fails against the previous code.

The remaining net/http load cost is socket (54 ms via net/protocol) and uri (10 ms), both of which it genuinely needs — this was the only removable item.

net/http requires resolv at the top of the file, but the only thing it
uses from that library is two regexp constants, on one line:

    case @address
    when Resolv::IPv4::Regex, Resolv::IPv6::Regex

That line lives in the TLS branch of #connect, so resolv is not needed
until an HTTPS connection is opened, and not at all for plain HTTP or
for the many libraries that require net/http without connecting.

Switching to autoload matches what the very next line of the file
already does for OpenSSL, and it keeps Resolv resolvable for anything
downstream that relied on net/http defining it -- the constant still
works, the load just happens on first reference.

Measured on Ruby 4.0.6 (arm64-darwin), best of seven runs:

              require 'net/http'   files loaded
  before      76.94 ms             33
  after       70.54 ms             30
               -6.40 ms             -3   (8% faster)

Verified that requiring net/http no longer loads resolv, that
Resolv::IPv4::Regex still resolves afterwards and pulls the library in
on demand, and that a live HTTPS GET still succeeds.

Test suite: 201 tests, 0 failures, unchanged. The three added tests
cover all three properties; the first fails against the previous code.

Signed-off-by: Tim Smith <tsmith84@proton.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant