Bläddra i källkod

Fix #494 Merge pull request #514 from mlh758/fix-494-write-allocations

Reduce allocations when writing
xuri 6 år sedan
förälder
incheckning
c8c8397751
4 ändrade filer med 51 tillägg och 16 borttagningar
  1. 5 13
      excelize.go
  2. 27 0
      file_test.go
  3. 15 3
      sheet.go
  4. 4 0
      xmlWorksheet.go

+ 5 - 13
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
 }

+ 27 - 0
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)
+		}
+	}
+
+}

+ 15 - 3
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]

+ 4 - 0
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.