Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement sass --embedded in pure JS mode #2413

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ntkme
Copy link
Contributor

@ntkme ntkme commented Oct 26, 2024

Closes #2325.

sass/embedded-host-node#344

Implementation

The actual isolate dispatcher and compilation dispatcher are nearly unchanged. However, I had to replace isolate with worker communication, and mock tons of small things that do not work on node.

Testing

  • All Dart embedded tests are passing. - GitHub CI has been updated to run these in this PR.
  • All JS API tests are passing. - GitHub CI has been updated to run these in this PR.
  • All Ruby API tests are passing.

@ntkme ntkme force-pushed the embedded-compiler branch 7 times, most recently from c9124f8 to b6d9e98 Compare October 29, 2024 19:36
@ntkme
Copy link
Contributor Author

ntkme commented Oct 30, 2024

Here are some benchmark results on 1.80.5 native binary vs pkg-npm-release (node v22.9.0).

I tested three cases. All of them on a single persisted compiler, lines that marked with /MT means multi-threaded (10 threads).

  1. Minimal Sass Input - compile a {b: c} 10000 times.
  2. High Round Trips - invoke host function 10 times per compilation, and run 1000 compilations.
  3. Bootstrap - compile bootstrap 50 times.

On single-threaded test Dart is much faster than pure JS, especially in the compute heavy bootstrap test. However, in nearly all multi-threaded test, pure JS is not far from Dart, which is actually great.

sass-embedded	1.80.5	(Embedded Host)	[Ruby]
dart-sass	1.80.5	(Sass Compiler)	[JavaScript]
--- Minimal Sass Input ---
                       user     system      total        real
sass-embedded      0.304688   0.081796   0.386484 (  2.195485)
sass-embedded/MT   0.546339   0.260653   0.806992 (  1.260589)
--- High Round Trips ---
                       user     system      total        real
sass-embedded      0.268202   0.084800   0.353002 (  2.135437)
sass-embedded/MT   0.316647   0.207332   0.523979 (  0.558180)
--- Bootstrap ---
                       user     system      total        real
sass-embedded      0.013900   0.006100   0.020000 ( 27.595710)
sass-embedded/MT   0.023358   0.012144   0.035502 (  3.378303)
sass-embedded	1.80.5	(Embedded Host)	[Ruby]
dart-sass	1.80.5	(Sass Compiler)	[Dart]
--- Minimal Sass Input ---
                       user     system      total        real
sass-embedded      0.255831   0.066576   0.322407 (  1.207566)
sass-embedded/MT   0.323200   0.218337   0.541537 (  0.578477)
--- High Round Trips ---
                       user     system      total        real
sass-embedded      0.197848   0.069268   0.267116 (  0.840963)
sass-embedded/MT   0.256294   0.226492   0.482786 (  0.521811)
--- Bootstrap ---
                       user     system      total        real
sass-embedded      0.013402   0.005420   0.018822 (  7.295685)
sass-embedded/MT   0.015503   0.008968   0.024471 (  2.538047)
Benchmark code
require 'benchmark'
require 'sass-embedded'

puts Sass.info

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

def run(n:, thread: 10, use_thread: false)
  if use_thread
    queue = Queue.new
    n.times do
      queue << nil
    end
    list = []
    thread.times do
      list << Thread.new do
        loop do
          queue.pop(true)
          yield
        rescue ThreadError
          break
        end
      end
    end
    list.each(&:join)
    queue.close
  else
    n.times do
      yield
    end
  end
end

def sass_embedded_minimal(use_thread: false)
  run(n: 10000, use_thread:) do
    Sass.compile_string('a {b: c}', style: :compressed, logger: Sass::Logger.silent).css
  end
end

def sass_embedded_round_trips(use_thread: false)
  run(n: 1000, use_thread:) do
    Sass.compile_string('@for $i from 1 through 10 { a:nth-child(#{$i}) { b: foo(); } }', functions: { 'foo()' => ->(_) { Sass::Value::String.new('test') } }, style: :compressed, logger: Sass::Logger.silent).css
  end
end

def sass_embedded_bootstrap(use_thread: false)
  run(n: 50, use_thread:) do
    Sass.compile(BOOTSTRAP, style: :compressed, logger: Sass::Logger.silent).css
  end
end

puts '--- Minimal Sass Input ---'
Benchmark.bm(16) do |x|
  GC.start

  x.report('sass-embedded') do
    sass_embedded_minimal
  end

  GC.start

  x.report('sass-embedded/MT') do
    sass_embedded_minimal(use_thread: true)
  end
end

puts '--- High Round Trips ---'
Benchmark.bm(16) do |x|
  GC.start

  x.report('sass-embedded') do
    sass_embedded_round_trips
  end

  GC.start

  x.report('sass-embedded/MT') do
    sass_embedded_round_trips(use_thread: true)
  end
end

puts '--- Bootstrap ---'
Benchmark.bm(16) do |x|
  GC.start

  x.report('sass-embedded') do
    sass_embedded_bootstrap
  end

  GC.start

  x.report('sass-embedded/MT') do
    sass_embedded_bootstrap(use_thread: true)
  end
end

@ntkme ntkme force-pushed the embedded-compiler branch 11 times, most recently from 9cc1a2f to 1586bbd Compare November 4, 2024 20:14
@nex3
Copy link
Contributor

nex3 commented Nov 4, 2024

This PR is on my radar, it's just been a very review-heavy few weeks for me and this is unfortunately on the low end of the priority spectrum.

@ntkme ntkme force-pushed the embedded-compiler branch 3 times, most recently from deb8145 to afddecc Compare November 5, 2024 00:32
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.

Implement sass --embedded in pure JS mode
2 participants