require 'rake/rdoctask' require 'rake/testtask' require 'rake/packagetask' require 'rake/gempackagetask' require 'rbconfig' require 'fileutils' $:.unshift 'lib' require 'ole/storage' PKG_NAME = 'ruby-ole' PKG_VERSION = Ole::Storage::VERSION task :default => [:test] Rake::TestTask.new do |t| t.test_files = FileList["test/test_*.rb"] t.warning = true t.verbose = true end begin require 'rcov/rcovtask' # NOTE: this will not do anything until you add some tests desc "Create a cross-referenced code coverage report" Rcov::RcovTask.new do |t| t.test_files = FileList['test/test*.rb'] t.ruby_opts << "-Ilib" # in order to use this rcov t.rcov_opts << "--xrefs" # comment to disable cross-references t.verbose = true end rescue LoadError # Rcov not available end Rake::RDocTask.new do |t| t.rdoc_dir = 'doc' t.rdoc_files.include 'lib/**/*.rb' t.rdoc_files.include 'README', 'ChangeLog' t.title = "#{PKG_NAME} documentation" t.options += %w[--line-numbers --inline-source --tab-width 2] t.main = 'README' end spec = Gem::Specification.new do |s| s.name = PKG_NAME s.version = PKG_VERSION s.summary = %q{Ruby OLE library.} s.description = %q{A library for easy read/write access to OLE compound documents for Ruby.} s.authors = ['Charles Lowe'] s.email = %q{aquasync@gmail.com} s.homepage = %q{http://code.google.com/p/ruby-ole} s.rubyforge_project = %q{ruby-ole} s.executables = ['oletool'] s.files = ['README', 'Rakefile', 'ChangeLog', 'data/propids.yaml'] s.files += FileList['lib/**/*.rb'] s.files += FileList['test/test_*.rb', 'test/*.doc'] s.files += FileList['test/oleWithDirs.ole', 'test/test_SummaryInformation'] s.files += FileList['bin/*'] s.test_files = FileList['test/test_*.rb'] s.has_rdoc = true s.extra_rdoc_files = ['README', 'ChangeLog'] s.rdoc_options += [ '--main', 'README', '--title', "#{PKG_NAME} documentation", '--tab-width', '2' ] end Rake::GemPackageTask.new(spec) do |t| t.gem_spec = spec t.need_tar = true t.need_zip = false t.package_dir = 'build' end desc 'Run various benchmarks' task :benchmark do require 'benchmark' require 'tempfile' require 'ole/file_system' # should probably add some read benchmarks too def write_benchmark opts={} files, size = opts[:files], opts[:size] block_size = opts[:block_size] || 100_000 block = 0.chr * block_size blocks, remaining = size.divmod block_size remaining = 0.chr * remaining Tempfile.open 'ole_storage_benchmark' do |temp| Ole::Storage.open temp do |ole| files.times do |i| ole.file.open "file_#{i}", 'w' do |f| blocks.times { f.write block } f.write remaining end end end end end Benchmark.bm do |bm| bm.report 'write_1mb_1x5' do 5.times { write_benchmark :files => 1, :size => 1_000_000 } end bm.report 'write_1mb_2x5' do 5.times { write_benchmark :files => 1_000, :size => 1_000 } end end end =begin 1.2.1: user system total real write_1mb_1x5 73.920000 8.400000 82.320000 ( 91.893138) revision 17 (speed up AllocationTable#free_block by using @sparse attribute, and using Array#index otherwise): user system total real write_1mb_1x5 57.910000 6.190000 64.100000 ( 66.207993) write_1mb_2x5266.310000 31.750000 298.060000 (305.877203) add in extra resize_chain fix (return blocks to avoid calling AllocationTable#chain twice): user system total real write_1mb_1x5 43.140000 5.480000 48.620000 ( 51.835942) add in RangesIOResizeable fix (cache @blocks, to avoid calling AllocationTable#chain at all when resizing now, just pass it to AllocationTable#resize_chain): user system total real write_1mb_1x5 29.770000 5.180000 34.950000 ( 39.916747) 40 seconds is still a really long time to write out 5 megs. of course, this is all with a 1_000 byte block size, which is a very small wite. upping this to 100_000 bytes: user system total real write_1mb_1x5 0.540000 0.130000 0.670000 ( 1.051862) so it seems that that makes a massive difference. so i really need buffering in RangesIO if I don't want it to really hurt for small writes, as all the resize code is kind of expensive. one of the costly things at the moment, is RangesIO#offset_and_size, which is called for each write, and re-finds which range we are in. that should obviously be changed, to a fixed one that is invalidated on seeks. buffering would hide that problem to some extent, but i should fix it anyway. re-running the original 1.2.1 with 100_000 byte block size: user system total real write_1mb_1x5 15.590000 2.230000 17.820000 ( 18.704910) so there the really badly non-linear AllocationTable#resize_chain is being felt. back to current working copy, running full benchmark: user system total real write_1mb_1x5 0.530000 0.150000 0.680000 ( 0.708919) write_1mb_2x5227.940000 31.260000 259.200000 (270.200960) not surprisingly, the second case hasn't been helped much by the fixes so far, as they only really help multiple resizes and writes for a file. this could be pain in the new file system code - potentially searching through Dirent#children at creation time. to test, i'll profile creating 1_000 files, without writing anything: user system total real write_1mb_2x5 16.990000 1.830000 18.820000 ( 19.900568) hmmm, so thats not all of it. maybe its the initial chain calls, etc? writing 1 byte: user system total real write_1mb_1x5 0.520000 0.120000 0.640000 ( 0.660638) write_1mb_2x5 19.810000 2.280000 22.090000 ( 22.696214) weird. 100 bytes: user system total real write_1mb_1x5 0.560000 0.140000 0.700000 ( 1.424974) write_1mb_2x5 22.940000 2.840000 25.780000 ( 26.556346) 500 bytes: user system total real write_1mb_1x5 0.530000 0.150000 0.680000 ( 1.139738) write_1mb_2x5 77.260000 10.130000 87.390000 ( 91.671086) what happens there? very strange. =end