Skip to content

Conversation

@iggant
Copy link
Contributor

@iggant iggant commented Feb 1, 2016

Can you please have a look at this propose:

After reading http://blog.codinghorror.com/zopfli-optimization-literally-free-bandwidth/ this article, I've started experimenting with zopfli gzip optimization.

and I have this parameters:

Zopfli gzip:

real    7m58.097s
user    7m48.060s
sys 0m7.475s


17 228 872 B, objects count: 1 383
3 883 929 B, objects count: 626 (only gz files)

Normal gzip

real    6m54.173s
user    6m47.092s
sys 0m5.910s

17 357 756 B, objects count: 1 383
4 012 813 B, objects count: 626 (only gz files)

So we have 3,31% size improvment, but this takes about 14% longer to compile
I don't add zopfli as default archiver but if you want, you'll be able to process gz, using zopfli archiver.

@schneems
Copy link
Member

schneems commented Feb 1, 2016

Since zopfli still generates gzip output I don't think we need to have another class i.e. i think the "gzip" name class name is fine. What if we abstract out the actual archiver parts into their own class and give them a call interface like

# utils/compression/zlib_archiver.rb
#...

class ZlibArchiver
  def call
    # ...
  end
end

Then we could have a zopfli_archiver etc. and if someone wanted to add their own custom compressor they would have an easy hook. We could change the method signature of the Gzip class can take a class as a second argument

module Sprockets
  module Utils
    class Gzip
      # Private: Generates a gzipped file based off of reference file.
      def initialize(asset, archiver = ZlibArchiver)
        @archiver = archiver
# ...

      def compress(target)
        mtime = PathUtils.stat(target).mtime

        PathUtils.atomic_write("#{target}.gz") do |f|
          @archiver.call(target: target, source: @source, mtime: mtime)
          File.utime(mtime, mtime, f.path)
        end
        nil
      end

That way the logic of if an object can be compressed and the atomic writing will stay in this gzip object, but the logic of the actual compression can live in a purpose built module/class/proc.

We could then use this inside of the manifest like this

next if environment.skip_gzip?
gzip = Utils::Gzip.new(asset, environment.gzip_compressor)

@iggant
Copy link
Contributor Author

iggant commented Feb 2, 2016

@schneems yes, now this is much better,
can pass to gzip_compressor any class/module/proc which respond to call method with 3 parameters, file, source and mtime.

@nateberkopec
Copy link
Contributor

I like it. For applications that don't compile assets in production (which they shouldn't do anyway!), we should be willing to trade compilation time for reduced asset size.

Needs a rebase.

@iggant
Copy link
Contributor Author

iggant commented Apr 24, 2016

@nateberkopec
I've made a liitle change, please have a look in this PR #241

@vincentwoo
Copy link

I think this would be a big, easy win for all Sprockets users. Has there been any progress on merging this?

@hansottowirtz
Copy link
Contributor

I made a gem that is based on #372: https://github.com/hansottowirtz/sprockets-exporters. It lets you add a ZopfliExporter, which was based on your code.

@schneems
Copy link
Member

schneems commented Sep 6, 2016

I think this would be a big, easy win for all Sprockets users. Has there been any progress on merging this?

Looks like it needs a rebase. There's also a few issues we mentioned above.

@iggant
Copy link
Contributor Author

iggant commented Sep 7, 2016

@schneems
can you please look here #241 I think that 241 has a better way to do this, also it introduce a not only zopfli but also webp, etc generation based on file.

@schneems
Copy link
Member

Zopfli is now in master

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.

5 participants