fast_excel 0.1.7 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56b8736c80626c089a7b20544519504306eaf41f
4
- data.tar.gz: 3acc3eceba37ae987a269a75b846713624d6d3fc
3
+ metadata.gz: 6c5919483e8d5ac1277efefe8399926f14b45d10
4
+ data.tar.gz: 2a8bd5f6067c8903778f3f7227f90d4c95d31868
5
5
  SHA512:
6
- metadata.gz: 9d4d65513075a6935d344f6d40193db2a4095c3ea66ecb36e5eff2308660704741257b46b83b6535e83c571d945e1f1ada48c95db9aeea48c2eb0299a89b3fc4
7
- data.tar.gz: 67792c48ae5170cffb0b8f51701f6e0f44489ed7f7d6b4a55ca814f32e987b271053755939d9fde4e4230ad7ab554827d5f9d9df962df9eb74a1ae47d194602b
6
+ metadata.gz: dd0f959fe4eb08bd7506f63b4f666c3d47c05765aa8213c775f873264dd561e8aa2c9dd7f61d1ea5f1e6498199248dfef80bb9b5f7693025ec0c3f192671dd27
7
+ data.tar.gz: 5168323f914545536e1f36ed30aa0f689a98e92a93875abb7137109c20c2adfbd78b905a6cf6339c36e9ae391c23d7461e24a6d15ad92cc28e43ef955b16ce1c
data/Gemfile CHANGED
@@ -1,9 +1,15 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'ffi'
4
- gem 'ffi_gen'
5
- gem 'axlsx'
4
+ gem 'ffi_gen', require: false
5
+
6
+ gem 'roo', '2.7.1', git: 'https://github.com/roo-rb/roo.git'
6
7
 
7
- gem 'roo'
8
8
  gem 'minitest'
9
- gem 'minitest-reporters'
9
+ gem 'minitest-reporters'
10
+
11
+ # For benchmakrs
12
+ gem 'axlsx', git: 'https://github.com/randym/axlsx.git'
13
+ gem 'write_xlsx'
14
+ gem 'benchmark-ips'
15
+ gem 'process_memory', git: 'https://github.com/paxa/process_memory'
data/Gemfile.lock CHANGED
@@ -1,16 +1,38 @@
1
+ GIT
2
+ remote: https://github.com/paxa/process_memory
3
+ revision: 1aa4df28c9a98903301317236104abe591e086fd
4
+ specs:
5
+ process_memory (0.1)
6
+
7
+ GIT
8
+ remote: https://github.com/randym/axlsx.git
9
+ revision: c8ac844572b25fda358cc01d2104720c4c42f450
10
+ specs:
11
+ axlsx (2.1.0.pre)
12
+ htmlentities (~> 4.3.4)
13
+ mimemagic (~> 0.3)
14
+ nokogiri (>= 1.6.6)
15
+ rubyzip (>= 1.2.1)
16
+
17
+ GIT
18
+ remote: https://github.com/roo-rb/roo.git
19
+ revision: 6bde8588dfd97d7a18981ee3ac7b444fec165a2c
20
+ specs:
21
+ roo (2.7.1)
22
+ nokogiri (~> 1)
23
+ rubyzip (>= 1.2.1, < 2.0.0)
24
+
1
25
  GEM
2
26
  remote: https://rubygems.org/
3
27
  specs:
4
28
  ansi (1.5.0)
5
- axlsx (2.0.1)
6
- htmlentities (~> 4.3.1)
7
- nokogiri (>= 1.4.1)
8
- rubyzip (~> 1.0.0)
29
+ benchmark-ips (2.7.2)
9
30
  builder (3.2.3)
10
- ffi (1.9.17)
31
+ ffi (1.9.18)
11
32
  ffi_gen (1.2.0)
12
33
  ffi (~> 1.0)
13
34
  htmlentities (4.3.4)
35
+ mimemagic (0.3.2)
14
36
  mini_portile2 (2.1.0)
15
37
  minitest (5.10.1)
16
38
  minitest-reporters (1.1.14)
@@ -18,28 +40,29 @@ GEM
18
40
  builder
19
41
  minitest (>= 5.0)
20
42
  ruby-progressbar
21
- nokogiri (1.7.0.1)
43
+ nokogiri (1.7.1)
22
44
  mini_portile2 (~> 2.1.0)
23
- roo (1.13.2)
24
- nokogiri
25
- rubyzip
26
- spreadsheet (> 0.6.4)
27
- ruby-ole (1.2.12.1)
28
45
  ruby-progressbar (1.8.1)
29
- rubyzip (1.0.0)
30
- spreadsheet (1.1.4)
31
- ruby-ole (>= 1.0)
46
+ rubyzip (1.2.1)
47
+ write_xlsx (0.83.0)
48
+ rubyzip (>= 1.0.0)
49
+ zip-zip
50
+ zip-zip (0.3)
51
+ rubyzip (>= 1.0.0)
32
52
 
33
53
  PLATFORMS
34
54
  ruby
35
55
 
36
56
  DEPENDENCIES
37
- axlsx
57
+ axlsx!
58
+ benchmark-ips
38
59
  ffi
39
60
  ffi_gen
40
61
  minitest
41
62
  minitest-reporters
42
- roo
63
+ process_memory!
64
+ roo (= 2.7.1)!
65
+ write_xlsx
43
66
 
44
67
  BUNDLED WITH
45
68
  1.14.5
data/Makefile CHANGED
@@ -8,7 +8,7 @@ all :
8
8
  $(Q)$(MAKE) -C libxlsxwriter
9
9
 
10
10
  clean :
11
- #$(Q)$(MAKE) clean -C libxlsxwriter
11
+ $(Q)$(MAKE) clean -C libxlsxwriter
12
12
 
13
13
  install :
14
14
  @echo "Nothing to install"
data/README.md CHANGED
@@ -22,18 +22,6 @@ worksheet.write_row(0, ["message", "price", "date"], bold)
22
22
 
23
23
  for i in 1..1000
24
24
  worksheet.write_row(i, ["Hello", (rand * 10_000_000).round(2), Time.now])
25
-
26
- # Or manually
27
- # worksheet.write_string(i, 0, "Hello", nil)
28
- # worksheet.write_number(i, 1, (rand * 10_000_000).round(2), nil)
29
- # date = Libxlsxwriter::Datetime.new
30
- # date[:year] = 2017
31
- # date[:month] = 2
32
- # date[:day] = 24
33
- # date[:hour] = i % 24
34
- # date[:min] = i % 60
35
- # date[:sec] = i % 60
36
- # worksheet.write_datetime(i, 2, date, nil)
37
25
  end
38
26
 
39
27
  workbook.close
@@ -41,3 +29,63 @@ workbook.close
41
29
 
42
30
  This repositiry and gem contain sources of [libxlsxwriter](https://github.com/jmcnamara/libxlsxwriter)
43
31
 
32
+ ## Benchmarks
33
+
34
+ 1000 rows:
35
+ ```
36
+ Comparison:
37
+ FastExcel: 31.7 i/s
38
+ Axslx: 8.0 i/s - 3.98x slower
39
+ write_xlsx: 6.9 i/s - 4.62x slower
40
+ ```
41
+
42
+ 20000 rows:
43
+ ```
44
+ Comparison:
45
+ FastExcel: 1.4 i/s
46
+ Axslx: 0.4 i/s - 3.46x slower
47
+ write_xlsx: 0.1 i/s - 17.04x slower
48
+ ```
49
+
50
+ Max memory usage, generating 100k rows:
51
+ ```
52
+ FastExcel - 20 MB
53
+ Axslx - 60 MB
54
+ write_axslx - 100 MB
55
+ ```
56
+
57
+ ## Install
58
+
59
+ ```ruby
60
+ # Gemfile
61
+ gem 'fast_excel'
62
+ ```
63
+ Or
64
+ ```
65
+ gem install fast_excel
66
+ ```
67
+
68
+ ## API
69
+
70
+ This gem is FFI binding for libxlsxwriter C library with some syntax sugar. All original functions is avaliable, for example:
71
+
72
+ ```ruby
73
+ Libxlsxwriter.worksheet_activate(worksheet) # => will call void worksheet_activate(lxw_worksheet *worksheet)
74
+ # or shorter:
75
+ worksheet.activate
76
+ ```
77
+
78
+ Full libxlsxwriter documentation: [http://libxlsxwriter.github.io/](http://libxlsxwriter.github.io/)
79
+
80
+ Helper Methods:
81
+
82
+ * `FastExcel.open(filename = nil, constant_memory: false, default_format: {})` - open new workbook, if `filename` is nil - it will craete tmp file, `default_format` will be called with `workbook.default_format.set(...)`
83
+ * `FastExcel.date_num(time, offset = nil)` - generate Excel's internal date value, number of days since 1900-Jan-01, works faster then creating `Libxlsxwriter::Datetime` struct. `offset` argument is number hours from UTC, e.g. `3.5`
84
+ * `FastExcel.print_ffi_obj(object)` - print FFI object fields, just for debugging
85
+ * `workbook.bold_cell_format` - shortcut for creating bold format
86
+ * `workbook.number_format(num_format)` - create number or date format, for money usually: `"#,##0.00"`, for date: `"[$-409]m/d/yy h:mm AM/PM;@"`
87
+ * `workbook.read_string` - close workbook, read file to string, delete file (only if tmp file)
88
+ * `workbook.remove_tmp_file` - delete tmp file (only if tmp file)
89
+ * `worksheet.write_row(row_num, array_of_mixed_value, formats = nil)` - write values one by one, detecting type automatically. `formats` can be array, or Format object or nil
90
+ * `format.font_family` - alias for `format.font_name`
91
+ * `workbook.default_format.font_size` - set it to 0 if you want to use default font size (that what user set in Excel settings)
data/Rakefile CHANGED
@@ -1,31 +1,4 @@
1
- namespace :build do
2
- desc "Build mac binary"
3
- task :mac do
4
- Dir.chdir("./libxlsxwriter") do
5
- system("make clean && make")
6
- system('cp lib/libxlsxwriter.dylib ../binaries/libxlsxwriter-darwin.dylib')
7
- end
8
- end
9
-
10
- desc "Build linux (libc) binary"
11
- task :linux do
12
- system("docker build . -f build_centos_linux.docker -t fast_excel_centos")
13
- system("docker run -t fast_excel_centos readelf -d lib/libxlsxwriter.so")
14
- last_container_id = `docker ps -a | grep fast_excel_centos | head -1 | awk '{print $1;}'`.strip
15
- system("docker cp #{last_container_id}:/srv/libxlsxwriter/lib/libxlsxwriter.so ./binaries/libxlsxwriter-glibc.so")
16
- system("docker rm #{last_container_id}")
17
- end
18
-
19
- desc "Build linux (musl) binary"
20
- task :linux_musl do
21
- system("docker build . -f build_alpine_linux.docker -t fast_excel_alpine")
22
- system("docker run -t fast_excel_alpine readelf -d lib/libxlsxwriter.so")
23
- last_container_id = `docker ps -a | grep fast_excel_alpine | head -1 | awk '{print $1;}'`.strip
24
- system("docker cp #{last_container_id}:/srv/libxlsxwriter/lib/libxlsxwriter.so ./binaries/libxlsxwriter-alpine.so")
25
- system("docker rm #{last_container_id}")
26
- end
27
- end
28
-
1
+ desc "Sync github.com:Paxa/libxlsxwriter to ./libxlsxwriter"
29
2
  task :sync do
30
3
  require 'fileutils'
31
4
  FileUtils.rm_rf("./libxlsxwriter")
@@ -34,4 +7,12 @@ task :sync do
34
7
  system("git show --pretty='format:%cd %h' --date=iso --quiet > version.txt")
35
8
  FileUtils.rm_rf("./.git")
36
9
  end
37
- end
10
+ end
11
+
12
+ require 'rake/testtask'
13
+
14
+ Rake::TestTask.new do |test|
15
+ test.test_files = Dir.glob('test/**/*_test.rb')
16
+ end
17
+
18
+ #task :default => :test
@@ -0,0 +1,59 @@
1
+ require_relative 'init'
2
+
3
+ HEADERS = ["id", "name", "age", "date"]
4
+
5
+ DATA = []
6
+ 1000.times do |n|
7
+ DATA << [n, "String string #{n}", (n * rand * 10).round, Time.at(n * 1000 + 1492922688)]
8
+ end
9
+
10
+ Benchmark.ips do |x|
11
+ x.config(time: 10, warmup: 2)
12
+
13
+ x.report("FastExcel") do
14
+ workbook = FastExcel.open(constant_memory: true)
15
+ worksheet = workbook.add_worksheet("benchmark")
16
+
17
+ worksheet.write_row(0, HEADERS)
18
+ DATA.each_with_index do |row, i|
19
+ worksheet.write_row(i + 1, row)
20
+ end
21
+ workbook.read_string
22
+ end
23
+
24
+ x.report("Axslx") do
25
+ filename = "#{Dir.mktmpdir}/axslx.xslx"
26
+ Axlsx::Package.new do |package|
27
+ package.use_autowidth = false
28
+ package.workbook.add_worksheet do |sheet|
29
+ sheet.add_row(HEADERS)
30
+ DATA.each do |row|
31
+ sheet.add_row(row)
32
+ end
33
+ end
34
+ package.serialize(filename)
35
+ File.open(filename, 'rb', &:read)
36
+ File.delete(filename)
37
+ end
38
+ end
39
+
40
+ x.report("write_xlsx") do
41
+ filename = "#{Dir.mktmpdir}/write_xlsx.xslx"
42
+ workbook = WriteXLSX.new(filename)
43
+ worksheet = workbook.add_worksheet
44
+ HEADERS.each_with_index do |value, i|
45
+ worksheet.write(0, i, value)
46
+ end
47
+ DATA.each_with_index do |row, row_num|
48
+ worksheet.write_number(row_num + 1, 0, row[0])
49
+ worksheet.write_string(row_num + 1, 1, row[1])
50
+ worksheet.write_number(row_num + 1, 2, row[2])
51
+ worksheet.write_number(row_num + 1, 3, row[3])
52
+ end
53
+ workbook.close
54
+ File.open(filename, 'rb', &:read)
55
+ File.delete(filename)
56
+ end
57
+
58
+ x.compare!
59
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'init'
2
+
3
+ HEADERS = ["id", "name", "age", "date", "random"]
4
+
5
+ DATA = []
6
+ 20_000.times do |n|
7
+ DATA << [n, "String string #{n}" * 5, (n * rand * 10).round, Time.at(n * 1000 + 1492922688), n * 100]
8
+ end
9
+
10
+ Benchmark.ips do |x|
11
+ x.config(time: 10, warmup: 2)
12
+
13
+ x.report("FastExcel") do
14
+ write_fast_excel_20k
15
+ end
16
+
17
+ x.report("Axslx") do
18
+ write_axslx_20k
19
+ end
20
+
21
+ x.report("write_xlsx") do
22
+ write_xlsx_20k
23
+ end
24
+
25
+ x.compare!
26
+ end
@@ -0,0 +1,59 @@
1
+ require 'bundler/setup'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require_relative '../lib/fast_excel'
5
+
6
+ # gem install axlsx benchmark-ips write_xlsx
7
+
8
+ require "benchmark/ips"
9
+ require 'axlsx'
10
+ require 'write_xlsx'
11
+ require 'process_memory'
12
+
13
+ require_relative 'init'
14
+
15
+ def write_fast_excel_20k
16
+ workbook = FastExcel.open(constant_memory: true)
17
+ worksheet = workbook.add_worksheet("benchmark")
18
+
19
+ worksheet.write_row(0, HEADERS)
20
+ DATA.each_with_index do |row, i|
21
+ worksheet.write_row(i + 1, row)
22
+ end
23
+ workbook.read_string
24
+ end
25
+
26
+ def write_xlsx_20k
27
+ filename = "#{Dir.mktmpdir}/write_xlsx.xslx"
28
+ workbook = WriteXLSX.new(filename)
29
+ worksheet = workbook.add_worksheet
30
+ HEADERS.each_with_index do |value, i|
31
+ worksheet.write(0, i, value)
32
+ end
33
+ DATA.each_with_index do |row, row_num|
34
+ worksheet.write_number(row_num + 1, 0, row[0])
35
+ worksheet.write_string(row_num + 1, 1, row[1])
36
+ worksheet.write_number(row_num + 1, 2, row[2])
37
+ worksheet.write_number(row_num + 1, 3, row[3])
38
+ worksheet.write_number(row_num + 1, 4, row[4])
39
+ end
40
+ workbook.close
41
+ File.open(filename, 'rb', &:read)
42
+ File.delete(filename)
43
+ end
44
+
45
+ def write_axslx_20k
46
+ filename = "#{Dir.mktmpdir}/axslx.xslx"
47
+ Axlsx::Package.new do |package|
48
+ package.use_autowidth = false
49
+ package.workbook.add_worksheet do |sheet|
50
+ sheet.add_row(HEADERS)
51
+ DATA.each do |row|
52
+ sheet.add_row(row)
53
+ end
54
+ end
55
+ package.serialize(filename)
56
+ File.open(filename, 'rb', &:read)
57
+ File.delete(filename)
58
+ end
59
+ end
@@ -0,0 +1,49 @@
1
+ require_relative 'init'
2
+
3
+ HEADERS = ["id", "name", "age", "date", "random"]
4
+
5
+ DATA = []
6
+ 10_000.times do |n|
7
+ DATA << [n, "String string #{n}" * 5, (n * rand * 10).round, Time.at(n * 1000 + 1492922688), n * 100]
8
+ end
9
+
10
+ puts "warm up..."
11
+ write_fast_excel_20k
12
+ write_axslx_20k
13
+ write_xlsx_20k
14
+
15
+ DATA.clear
16
+ 100_000.times do |n|
17
+ DATA << [n, "String string #{n}" * 5, (n * rand * 10).round, Time.at(n * 1000 + 1492922688), n * 100]
18
+ end
19
+
20
+ GC.start
21
+ sleep 5
22
+
23
+ def measure_memory(title)
24
+ puts "Running test: #{title}"
25
+ recorder = ProcessMemory.start_recording
26
+ yield
27
+ puts recorder.print("Done!")
28
+ recorder.stop
29
+ puts recorder.report_per_second_pretty
30
+ puts
31
+ end
32
+
33
+ measure_memory("FastExcel") do
34
+ write_fast_excel_20k
35
+ end
36
+
37
+ GC.start
38
+ sleep 5
39
+
40
+ measure_memory("Axslx") do
41
+ write_axslx_20k
42
+ end
43
+
44
+ GC.start
45
+ sleep 5
46
+
47
+ measure_memory("write_axslx") do
48
+ write_axslx_20k
49
+ end