additional perf work

This commit is contained in:
Thomas Reynolds 2016-01-22 14:25:02 -08:00
parent e0818e2118
commit b8c0fd34e7
9 changed files with 99 additions and 53 deletions

View file

@ -52,7 +52,7 @@ module Middleman::Cli
::Middleman::Logger.singleton(verbose, instrument) ::Middleman::Logger.singleton(verbose, instrument)
::Middleman::Util.instrument 'builder_setup' do ::Middleman::Util.instrument 'builder.setup' do
@app = ::Middleman::Application.new do @app = ::Middleman::Application.new do
config[:mode] = :build config[:mode] = :build
config[:environment] = env config[:environment] = env
@ -67,7 +67,7 @@ module Middleman::Cli
builder.on_build_event(&method(:on_event)) builder.on_build_event(&method(:on_event))
end end
::Middleman::Util.instrument 'builder_run' do ::Middleman::Util.instrument 'builder.run' do
if builder.run! if builder.run!
clean_directories! if options['clean'] clean_directories! if options['clean']
shell.say 'Project built successfully.' shell.say 'Project built successfully.'

View file

@ -51,18 +51,31 @@ module Middleman
@has_error = false @has_error = false
@events = {} @events = {}
@app.execute_callbacks(:before_build, [self]) ::Middleman::Util.instrument 'builder.before' do
@app.execute_callbacks(:before_build, [self])
end
queue_current_paths if @cleaning ::Middleman::Util.instrument 'builder.queue' do
queue_current_paths if @cleaning
end
prerender_css ::Middleman::Util.instrument 'builder.prerender' do
output_files prerender_css
end
clean! if @cleaning ::Middleman::Util.instrument 'builder.output' do
output_files
end
::Middleman::Util.instrument 'builder.clean' do
clean! if @cleaning
end
::Middleman::Profiling.report('build') ::Middleman::Profiling.report('build')
@app.execute_callbacks(:after_build, [self]) ::Middleman::Util.instrument 'builder.after' do
@app.execute_callbacks(:after_build, [self])
end
!@has_error !@has_error
end end
@ -73,14 +86,18 @@ module Middleman
def prerender_css def prerender_css
logger.debug '== Prerendering CSS' logger.debug '== Prerendering CSS'
css_files = @app.sitemap.resources css_files = ::Middleman::Util.instrument 'builder.prerender.output' do
.select { |resource| resource.ext == '.css' } @app.sitemap.resources
.each(&method(:output_resource)) .select { |resource| resource.ext == '.css' }
.each(&method(:output_resource))
end
# Double-check for compass sprites ::Middleman::Util.instrument 'builder.prerender.check-files' do
if @app.files.find_new_files!.length > 0 # Double-check for compass sprites
logger.debug '== Checking for Compass sprites' if @app.files.find_new_files!.length > 0
@app.sitemap.ensure_resource_list_updated! logger.debug '== Checking for Compass sprites'
@app.sitemap.ensure_resource_list_updated!
end
end end
css_files css_files
@ -92,11 +109,15 @@ module Middleman
def output_files def output_files
logger.debug '== Building files' logger.debug '== Building files'
@app.sitemap.resources resources = @app.sitemap.resources
.sort_by { |resource| SORT_ORDER.index(resource.ext) || 100 }
.reject { |resource| resource.ext == '.css' } .reject { |resource| resource.ext == '.css' }
.select { |resource| !@glob || File.fnmatch(@glob, resource.destination_path) } .sort_by { |resource| SORT_ORDER.index(resource.ext) || 100 }
.each(&method(:output_resource))
if @glob
resources = resources.select { |resource| File.fnmatch(@glob, resource.destination_path) }
end
resources.each(&method(:output_resource))
end end
# Figure out the correct event mode. # Figure out the correct event mode.
@ -162,25 +183,29 @@ module Middleman
# @return [void] # @return [void]
Contract IsA['Middleman::Sitemap::Resource'] => Any Contract IsA['Middleman::Sitemap::Resource'] => Any
def output_resource(resource) def output_resource(resource)
output_file = @build_dir + resource.destination_path.gsub('%20', ' ') output_file = nil
begin ::Middleman::Util.instrument "builder.output.resource", path: File.basename(resource.destination_path) do
if resource.binary? output_file = @build_dir + resource.destination_path.gsub('%20', ' ')
export_file!(output_file, resource.file_descriptor[:full_path])
else
response = @rack.get(::URI.escape(resource.request_path))
# If we get a response, save it to a tempfile. begin
if response.status == 200 if resource.binary?
export_file!(output_file, binary_encode(response.body)) export_file!(output_file, resource.file_descriptor[:full_path])
else else
@has_error = true response = @rack.get(::URI.escape(resource.request_path))
trigger(:error, output_file, response.body)
# If we get a response, save it to a tempfile.
if response.status == 200
export_file!(output_file, binary_encode(response.body))
else
@has_error = true
trigger(:error, output_file, response.body)
end
end end
rescue => e
@has_error = true
trigger(:error, output_file, "#{e}\n#{e.backtrace.join("\n")}")
end end
rescue => e
@has_error = true
trigger(:error, output_file, "#{e}\n#{e.backtrace.join("\n")}")
end end
return unless @cleaning return unless @cleaning

View file

@ -57,6 +57,7 @@ module Middleman
@app = app @app = app
@data_file_matcher = data_file_matcher @data_file_matcher = data_file_matcher
@local_data = {} @local_data = {}
@local_data_enhanced = nil
@local_sources = {} @local_sources = {}
@callback_sources = {} @callback_sources = {}
end end
@ -117,6 +118,8 @@ module Middleman
end end
data_branch[basename] = data data_branch[basename] = data
@local_data_enhanced = nil
end end
# Remove a given file from the internal cache # Remove a given file from the internal cache
@ -137,6 +140,8 @@ module Middleman
end end
data_branch.delete(basename) if data_branch.key?(basename) data_branch.delete(basename) if data_branch.key?(basename)
@local_data_enhanced = nil
end end
# Get a hash from either internal static data or a callback # Get a hash from either internal static data or a callback
@ -151,8 +156,7 @@ module Middleman
callbacks[path.to_s].call callbacks[path.to_s].call
end end
response = ::Middleman::Util.recursively_enhance(response) ::Middleman::Util.recursively_enhance(response)
response
end end
# "Magically" find namespaces of data if they exist # "Magically" find namespaces of data if they exist
@ -162,7 +166,8 @@ module Middleman
def method_missing(path) def method_missing(path)
if @local_data.key?(path.to_s) if @local_data.key?(path.to_s)
# Any way to cache this? # Any way to cache this?
return ::Middleman::Util.recursively_enhance(@local_data[path.to_s]) @local_data_enhanced ||= ::Middleman::Util.recursively_enhance(@local_data)
return @local_data_enhanced[path.to_s]
else else
result = data_for_path(path) result = data_for_path(path)
return result if result return result if result

View file

@ -73,9 +73,10 @@ module Middleman
# end # end
# Render using Tilt # Render using Tilt
content = ::Middleman::Util.instrument 'render.tilt', path: path do # content = ::Middleman::Util.instrument 'render.tilt', path: path do
template.render(context, locs, &block) # template.render(context, locs, &block)
end # end
content = template.render(context, locs, &block)
# Allow hooks to manipulate the result after render # Allow hooks to manipulate the result after render
content = @app.callbacks_for(:after_render).reduce(content) do |sum, callback| content = @app.callbacks_for(:after_render).reduce(content) do |sum, callback|

View file

@ -40,6 +40,7 @@ module Middleman
return if @instrumenting.is_a?(String) && @instrumenting != 'instrument' && !message.include?(@instrumenting) return if @instrumenting.is_a?(String) && @instrumenting != 'instrument' && !message.include?(@instrumenting)
evt = ::ActiveSupport::Notifications::Event.new(message, *args) evt = ::ActiveSupport::Notifications::Event.new(message, *args)
return unless evt.duration > 30
info "== Instrument (#{evt.name.sub(/.middleman$/, '')}): #{evt.duration}ms\n#{args.last}" info "== Instrument (#{evt.name.sub(/.middleman$/, '')}): #{evt.duration}ms\n#{args.last}"
end end
end end

View file

@ -66,6 +66,8 @@ module Middleman
# Page are data that is exposed through this resource's data member. # Page are data that is exposed through this resource's data member.
# Note: It is named 'page' for backwards compatibility with older MM. # Note: It is named 'page' for backwards compatibility with older MM.
@metadata = { options: {}, locals: {}, page: {} } @metadata = { options: {}, locals: {}, page: {} }
@page_data = nil
end end
# Whether this resource has a template file # Whether this resource has a template file
@ -91,6 +93,7 @@ module Middleman
# Note: It is named 'page' for backwards compatibility with older MM. # Note: It is named 'page' for backwards compatibility with older MM.
Contract METADATA_HASH => METADATA_HASH Contract METADATA_HASH => METADATA_HASH
def add_metadata(meta={}) def add_metadata(meta={})
@page_data = nil
@metadata.deep_merge!(meta) @metadata.deep_merge!(meta)
end end
@ -98,7 +101,7 @@ module Middleman
# @return [Hash] # @return [Hash]
Contract RespondTo[:indifferent_access?] Contract RespondTo[:indifferent_access?]
def data def data
::Middleman::Util.recursively_enhance(metadata[:page]) @page_data ||= ::Middleman::Util.recursively_enhance(metadata[:page])
end end
# Options about how this resource is rendered, such as its :layout, # Options about how this resource is rendered, such as its :layout,
@ -129,7 +132,6 @@ module Middleman
def render(opts={}, locs={}) def render(opts={}, locs={})
return ::Middleman::FileRenderer.new(@app, file_descriptor[:full_path].to_s).template_data_for_file unless template? return ::Middleman::FileRenderer.new(@app, file_descriptor[:full_path].to_s).template_data_for_file unless template?
# ::Middleman::Util.instrument 'render.resource', path: file_descriptor[:full_path].to_s, destination_path: destination_path do
md = metadata md = metadata
opts = md[:options].deep_merge(opts) opts = md[:options].deep_merge(opts)
locs = md[:locals].deep_merge(locs) locs = md[:locals].deep_merge(locs)
@ -140,7 +142,6 @@ module Middleman
renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s) renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s)
renderer.render(locs, opts) renderer.render(locs, opts)
# end
end end
# A path without the directory index - so foo/index.html becomes # A path without the directory index - so foo/index.html becomes

View file

@ -185,9 +185,7 @@ module Middleman
new_files = ::Middleman::Util.all_files_under(@directory.to_s) new_files = ::Middleman::Util.all_files_under(@directory.to_s)
.reject { |p| @files.key?(p) } .reject { |p| @files.key?(p) }
update(new_files, []) update(new_files, []).flatten.map { |s| s[:full_path] }
new_files
end end
# Manually trigger update events. # Manually trigger update events.
@ -198,14 +196,14 @@ module Middleman
updated = ::Middleman::Util.all_files_under(@directory.to_s) updated = ::Middleman::Util.all_files_under(@directory.to_s)
removed = @files.keys.reject { |p| updated.include?(p) } removed = @files.keys.reject { |p| updated.include?(p) }
update(updated, removed) result = update(updated, removed)
if @waiting_for_existence && @directory.exist? if @waiting_for_existence && @directory.exist?
@waiting_for_existence = false @waiting_for_existence = false
listen! listen!
end end
updated + removed result.flatten.map { |s| s[:full_path] }
end end
# Work around this bug: http://bugs.ruby-lang.org/issues/4521 # Work around this bug: http://bugs.ruby-lang.org/issues/4521
@ -238,7 +236,7 @@ module Middleman
# #
# @param [String, Pathname] path The updated file path. # @param [String, Pathname] path The updated file path.
# @return [void] # @return [void]
Contract ArrayOf[Pathname], ArrayOf[Pathname] => Any Contract ArrayOf[Pathname], ArrayOf[Pathname] => ArrayOf[ArrayOf[IsA['Middleman::SourceFile']]]
def update(updated_paths, removed_paths) def update(updated_paths, removed_paths)
valid_updates = updated_paths valid_updates = updated_paths
.map { |p| @files[p] || path_to_source_file(p, @directory, @type, @options[:destination_dir]) } .map { |p| @files[p] || path_to_source_file(p, @directory, @type, @options[:destination_dir]) }
@ -271,6 +269,8 @@ module Middleman
valid_removes, valid_removes,
self self
]) unless valid_updates.empty? && valid_removes.empty? ]) unless valid_updates.empty? && valid_removes.empty?
[valid_updates, valid_removes]
end end
# Convert a path to a file resprentation. # Convert a path to a file resprentation.

View file

@ -133,12 +133,21 @@ module Middleman
# Add extension helpers to context. # Add extension helpers to context.
@app.extensions.add_exposed_to_context(context) @app.extensions.add_exposed_to_context(context)
content = _render_with_all_renderers(path, locs, context, opts, &block) content = ::Middleman::Util.instrument 'builder.output.resource.render-template', path: File.basename(path) do
_render_with_all_renderers(path, locs, context, opts, &block)
end
# If we need a layout and have a layout, use it # If we need a layout and have a layout, use it
if layout_file = fetch_layout(engine, options) layout_file = fetch_layout(engine, options)
layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s) if layout_file
content = layout_renderer.render(locals, options, context) { content } content = ::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do
if layout_file = fetch_layout(engine, options)
layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s)
layout_renderer.render(locals, options, context) { content }
else
content
end
end
end end
# Return result # Return result

View file

@ -98,7 +98,7 @@ module Middleman
# @private # @private
# @param [Hash] data Normal hash # @param [Hash] data Normal hash
# @return [Hash] # @return [Hash]
Contract Maybe[Hash] => Maybe[Or[Array, EnhancedHash]] Contract Any => Maybe[Or[Array, EnhancedHash]]
def recursively_enhance(obj) def recursively_enhance(obj)
if obj.is_a? ::Array if obj.is_a? ::Array
obj.map { |e| recursively_enhance(e) } obj.map { |e| recursively_enhance(e) }
@ -480,6 +480,8 @@ module Middleman
# @return [String] # @return [String]
Contract String => ArrayOf[String] Contract String => ArrayOf[String]
def collect_extensions(path) def collect_extensions(path)
return [] if File.basename(path).start_with?('.')
result = [] result = []
step_through_extensions(path) { |e| result << e } step_through_extensions(path) { |e| result << e }
@ -495,6 +497,8 @@ module Middleman
# @return [Middleman::SourceFile] All related file paths, not including the source file paths. # @return [Middleman::SourceFile] All related file paths, not including the source file paths.
Contract ::Middleman::Application, ArrayOf[Pathname] => ArrayOf[::Middleman::SourceFile] Contract ::Middleman::Application, ArrayOf[Pathname] => ArrayOf[::Middleman::SourceFile]
def find_related_files(app, files) def find_related_files(app, files)
return [] if files.empty?
all_extensions = files.flat_map { |f| collect_extensions(f.to_s) } all_extensions = files.flat_map { |f| collect_extensions(f.to_s) }
sass_type_aliasing = ['.scss', '.sass'] sass_type_aliasing = ['.scss', '.sass']