Browse Source

optimize memory allocation (#722)

* optimize marshal

* optimize mem alloc

* add benchmark testing

* add NewSheetWithRowNum testing

* sync struct fields order

* add BenchmarkNewSheetWithStreamWriter

* delete NewSheetWithRowNum and benchmark test
Ted 5 years ago
parent
commit
fcca8a3838
4 changed files with 57 additions and 13 deletions
  1. 2 1
      lib.go
  2. 10 2
      sheet.go
  3. 34 0
      sheet_test.go
  4. 11 10
      xmlWorksheet.go

+ 2 - 1
lib.go

@@ -206,8 +206,9 @@ func CoordinatesToCellName(col, row int) (string, error) {
 	if col < 1 || row < 1 {
 		return "", fmt.Errorf("invalid cell coordinates [%d, %d]", col, row)
 	}
+	//Using itoa will save more memory
 	colname, err := ColumnNumberToName(col)
-	return fmt.Sprintf("%s%d", colname, row), err
+	return colname + strconv.Itoa(row), err
 }
 
 // boolPtr returns a pointer to a bool with the given value.

+ 10 - 2
sheet.go

@@ -120,18 +120,26 @@ func (f *File) workBookWriter() {
 // workSheetWriter provides a function to save xl/worksheets/sheet%d.xml after
 // serialize structure.
 func (f *File) workSheetWriter() {
+
+	// optimize memory alloc
+	var arr []byte
+	buffer := bytes.NewBuffer(arr)
+	encoder := xml.NewEncoder(buffer)
+
 	for p, sheet := range f.Sheet {
 		if sheet != nil {
 			for k, v := range sheet.SheetData.Row {
 				f.Sheet[p].SheetData.Row[k].C = trimCell(v.C)
 			}
-			output, _ := xml.Marshal(sheet)
-			f.saveFileList(p, replaceRelationshipsBytes(f.replaceNameSpaceBytes(p, output)))
+			// reusing buffer
+			encoder.Encode(sheet)
+			f.saveFileList(p, replaceRelationshipsBytes(f.replaceNameSpaceBytes(p, buffer.Bytes())))
 			ok := f.checked[p]
 			if ok {
 				delete(f.Sheet, p)
 				f.checked[p] = false
 			}
+			buffer.Reset()
 		}
 	}
 }

+ 34 - 0
sheet_test.go

@@ -3,6 +3,7 @@ package excelize
 import (
 	"fmt"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"testing"
 
@@ -344,3 +345,36 @@ func TestSetSheetName(t *testing.T) {
 	f.SetSheetName("Sheet1", "Sheet1")
 	assert.Equal(t, "Sheet1", f.GetSheetName(0))
 }
+
+func BenchmarkNewSheet(b *testing.B) {
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			newSheetWithSet()
+		}
+	})
+}
+func newSheetWithSet() {
+	file := NewFile()
+	file.NewSheet("sheet1")
+	for i := 0; i < 1000; i++ {
+		file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i)
+	}
+	file = nil
+}
+
+func BenchmarkFile_SaveAs(b *testing.B) {
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			newSheetWithSave()
+		}
+
+	})
+}
+func newSheetWithSave() {
+	file := NewFile()
+	file.NewSheet("sheet1")
+	for i := 0; i < 1000; i++ {
+		file.SetCellInt("sheet1", "A"+strconv.Itoa(i+1), i)
+	}
+	file.Save()
+}

+ 11 - 10
xmlWorksheet.go

@@ -313,20 +313,20 @@ type xlsxSheetData struct {
 // xlsxRow directly maps the row element. The element expresses information
 // about an entire row of a worksheet, and contains all cell definitions for a
 // particular row in the worksheet.
-type xlsxRow struct {
-	Collapsed    bool    `xml:"collapsed,attr,omitempty"`
+type xlsxRow struct {	// alignment word
+	C            []xlsxC `xml:"c"`
+	R            int     `xml:"r,attr,omitempty"`
+	Spans        string  `xml:"spans,attr,omitempty"`
+	S            int     `xml:"s,attr,omitempty"`
 	CustomFormat bool    `xml:"customFormat,attr,omitempty"`
-	CustomHeight bool    `xml:"customHeight,attr,omitempty"`
-	Hidden       bool    `xml:"hidden,attr,omitempty"`
 	Ht           float64 `xml:"ht,attr,omitempty"`
+	Hidden       bool    `xml:"hidden,attr,omitempty"`
+	CustomHeight bool    `xml:"customHeight,attr,omitempty"`
 	OutlineLevel uint8   `xml:"outlineLevel,attr,omitempty"`
-	Ph           bool    `xml:"ph,attr,omitempty"`
-	R            int     `xml:"r,attr,omitempty"`
-	S            int     `xml:"s,attr,omitempty"`
-	Spans        string  `xml:"spans,attr,omitempty"`
-	ThickBot     bool    `xml:"thickBot,attr,omitempty"`
+	Collapsed    bool    `xml:"collapsed,attr,omitempty"`
 	ThickTop     bool    `xml:"thickTop,attr,omitempty"`
-	C            []xlsxC `xml:"c"`
+	ThickBot     bool    `xml:"thickBot,attr,omitempty"`
+	Ph           bool    `xml:"ph,attr,omitempty"`
 }
 
 // xlsxSortState directly maps the sortState element. This collection
@@ -456,6 +456,7 @@ type DataValidation struct {
 //      s (Shared String)         | Cell containing a shared string.
 //      str (String)              | Cell containing a formula string.
 //
+// fixme: how to make this structure smaller; cur size is 152 bytes. it's be too bigger.
 type xlsxC struct {
 	XMLName  xml.Name `xml:"c"`
 	XMLSpace xml.Attr `xml:"space,attr,omitempty"`