fast_excel 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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