Project

General

Profile

Actions

Bug #20604

closed

Performance regression in C++ extensions due to lack of optimization flags by default since Ruby 2.7

Added by ntkme (Natsuki Natsume) 15 days ago. Updated 6 days ago.

Status:
Rejected
Assignee:
-
Target version:
-
ruby -v:
ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [arm64-darwin23]
[ruby-core:118419]

Description

I found a significant performance regression in sassc gem when comparing Ruby 2.6 and later, that the extension is running more than 10x slower.

I have tracked it down to this commit: https://github.com/ruby/ruby/commit/733aa2f8b578d03bbcb91d2f496b01e3b990c7e8

This commit removed all default CXXFLAGS from RbConfig that were previously set, including $optflags, which has -O3.

Many of the C++ extensions like sassc use extconf/mkmf to compile, and they have assumed optflags are already set for CXXFLAGS. Without -O3 as part of the default, the extensions are compiled without any optimizations and thus become extremely slow.

It is difficult for a regular user to find out the reason of the slowness, nor to say figuring out how to override CXXFLAGS for a native extension.

I'm not sure what was the reason of the change, but I think we should at least set $optflags in CXXFLAGS by default.

Updated by ntkme (Natsuki Natsume) 9 days ago

This can be reproduced with the following benchmark script:

# frozen_string_literal: true

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'benchmark-ips'
  gem 'bootstrap', '~> 5.3'
  gem 'sassc'
end

BOOTSTRAP = Gem::Specification.find_by_name('bootstrap').gem_dir + '/assets/stylesheets/_bootstrap.scss'

require 'benchmark/ips'
require 'sassc'

Benchmark.ips do |x|
  x.time = 15

  x.report("CXXFLAGS=#{RbConfig::CONFIG["CXXFLAGS"].inspect}") do
    SassC::Engine.new(File.read(BOOTSTRAP), { style: :compressed, filename: BOOTSTRAP }).render
  end

  x.compare!
end

Begin with the default on ruby:3.3 docker container with gcc 12.2.0.

Please make sure to gem uninstall -a sassc before the test to start clean.

ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [aarch64-linux]
Warming up --------------------------------------
         CXXFLAGS=""     1.000 i/100ms
Calculating -------------------------------------
         CXXFLAGS=""      0.765 (± 0.0%) i/s -     12.000 in  15.684859s

Now edit $RUBYARCHDIR/rbconfig.rb to manually patch the default CXXFLAGS as the following:

  CONFIG["CXXFLAGS"] = "$(optflags)"

Note: This file is read-only by default, and may need to be force overwritten with depends on your editor.

Now we can run the same test again.

Please make sure to gem uninstall -a sassc before the test to start clean.

You can see this is about 7 times faster with $optflags on linux gcc.

ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [aarch64-linux]
Warming up --------------------------------------
CXXFLAGS="-O3 -fno-fast-math"
                         1.000 i/100ms
Calculating -------------------------------------
CXXFLAGS="-O3 -fno-fast-math"
                          5.483 (± 0.0%) i/s -     83.000 in  15.139519s

I also tested on mac with clang 15 using homebrew ruby 3.3 out of box:

ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [arm64-darwin23]
Warming up --------------------------------------
CXXFLAGS="-fdeclspec"
                         1.000 i/100ms
Calculating -------------------------------------
CXXFLAGS="-fdeclspec"
                          0.517 (± 0.0%) i/s -      8.000 in  15.475395s

It's more than 10 times faster with $optflags with clang 15 on mac:.

ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [arm64-darwin23]
Warming up --------------------------------------
CXXFLAGS="-fdeclspec -O3 -fno-fast-math"
                         1.000 i/100ms
Calculating -------------------------------------
CXXFLAGS="-fdeclspec -O3 -fno-fast-math"
                          5.601 (± 0.0%) i/s -     85.000 in  15.177061s

Updated by shyouhei (Shyouhei Urabe) 6 days ago

  • Status changed from Open to Rejected

This is NG. Some OSes (if I remember correctly it was Arch when we found this) ship clang and g++ combination. They happen to have -O3 in common but not everything. It was a wrong idea first place for us to share $optflags. There's nothing we can do here. Please specify $CXXFLAGS individually.

Updated by alanwu (Alan Wu) 6 days ago

Please specify $CXXFLAGS individually.

Just some additional information about how to do this.
If you maintain a gem, you can add the desired flags to $CXXFLAGS in your extconf.rb.

As an user, you can install a gem while adding to $CXXFLAGS using --with-cxxflags without changing the gem. This is an mkmf feature. For example:

$ gem install sassc -- --with-cxxflags=-O3
Actions

Also available in: Atom PDF

Like0
Like0Like0Like1