summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarris <mike.harris@cerner.com>2019-10-28 10:34:21 -0500
committerHarris <mike.harris@cerner.com>2019-11-05 08:18:11 -0600
commitbf9a8355494eac18812f3caf6d469962824f627f (patch)
tree023a4a3d98b0efcd38860a062f4fe26de81cbb81
parent6abf8bf9723512086f009ca574bde1d6682fc83d (diff)
Reduce allocations when writing
Fix #494 If a row is full, don't bother allocating a new one, just return it. Use the last populated row as a hint for the size of new rows. Simplify checkSheet to remove row map
-rw-r--r--excelize.go18
-rw-r--r--file_test.go27
-rw-r--r--sheet.go18
-rw-r--r--xmlWorksheet.go4
4 files changed, 51 insertions, 16 deletions
diff --git a/excelize.go b/excelize.go
index 4d46b94..ba6445f 100644
--- a/excelize.go
+++ b/excelize.go
@@ -155,20 +155,12 @@ func checkSheet(xlsx *xlsxWorksheet) {
row = lastRow
}
}
- sheetData := xlsxSheetData{}
- existsRows := map[int]int{}
- for k := range xlsx.SheetData.Row {
- existsRows[xlsx.SheetData.Row[k].R] = k
+ sheetData := xlsxSheetData{Row: make([]xlsxRow, row)}
+ for _, r := range xlsx.SheetData.Row {
+ sheetData.Row[r.R-1] = r
}
- for i := 0; i < row; i++ {
- _, ok := existsRows[i+1]
- if ok {
- sheetData.Row = append(sheetData.Row, xlsx.SheetData.Row[existsRows[i+1]])
- } else {
- sheetData.Row = append(sheetData.Row, xlsxRow{
- R: i + 1,
- })
- }
+ for i := 1; i <= row; i++ {
+ sheetData.Row[i-1].R = i
}
xlsx.SheetData = sheetData
}
diff --git a/file_test.go b/file_test.go
new file mode 100644
index 0000000..6c30f4a
--- /dev/null
+++ b/file_test.go
@@ -0,0 +1,27 @@
+package excelize
+
+import (
+ "testing"
+)
+
+func BenchmarkWrite(b *testing.B) {
+ const s = "This is test data"
+ for i := 0; i < b.N; i++ {
+ f := NewFile()
+ for row := 1; row <= 10000; row++ {
+ for col := 1; col <= 20; col++ {
+ val, err := CoordinatesToCellName(col, row)
+ if err != nil {
+ panic(err)
+ }
+ f.SetCellDefault("Sheet1", val, s)
+ }
+ }
+ // Save xlsx file by the given path.
+ err := f.SaveAs("./test.xlsx")
+ if err != nil {
+ panic(err)
+ }
+ }
+
+}
diff --git a/sheet.go b/sheet.go
index 335c4fc..43c7cc0 100644
--- a/sheet.go
+++ b/sheet.go
@@ -117,12 +117,19 @@ func (f *File) workSheetWriter() {
}
}
-// trimCell provides a function to trim blank cells which created by completeCol.
+// trimCell provides a function to trim blank cells which created by fillColumns.
func trimCell(column []xlsxC) []xlsxC {
+ rowFull := true
+ for i := range column {
+ rowFull = column[i].hasValue() && rowFull
+ }
+ if rowFull {
+ return column
+ }
col := make([]xlsxC, len(column))
i := 0
for _, c := range column {
- if c.S != 0 || c.V != "" || c.F != nil || c.T != "" {
+ if c.hasValue() {
col[i] = c
i++
}
@@ -1404,12 +1411,17 @@ func (f *File) relsReader(path string) *xlsxRelationships {
// fillSheetData ensures there are enough rows, and columns in the chosen
// row to accept data. Missing rows are backfilled and given their row number
+// Uses the last populated row as a hint for the size of the next row to add
func prepareSheetXML(xlsx *xlsxWorksheet, col int, row int) {
rowCount := len(xlsx.SheetData.Row)
+ sizeHint := 0
+ if rowCount > 0 {
+ sizeHint = len(xlsx.SheetData.Row[rowCount-1].C)
+ }
if rowCount < row {
// append missing rows
for rowIdx := rowCount; rowIdx < row; rowIdx++ {
- xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{R: rowIdx + 1})
+ xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{R: rowIdx + 1, C: make([]xlsxC, 0, sizeHint)})
}
}
rowData := &xlsx.SheetData.Row[row-1]
diff --git a/xmlWorksheet.go b/xmlWorksheet.go
index 96ca235..8408cfa 100644
--- a/xmlWorksheet.go
+++ b/xmlWorksheet.go
@@ -430,6 +430,10 @@ type xlsxC struct {
XMLSpace xml.Attr `xml:"space,attr,omitempty"`
}
+func (c *xlsxC) hasValue() bool {
+ return c.S != 0 || c.V != "" || c.F != nil || c.T != ""
+}
+
// xlsxF directly maps the f element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - currently I have
// not checked it for completeness - it does as much as I need.