From 1ab06a69cab74c519e218963e80b99d10688ab13 Mon Sep 17 00:00:00 2001 From: Wondertan Date: Thu, 25 Jul 2024 01:51:59 +0200 Subject: [PATCH 1/6] simplify quadrant buffering --- store/file/ods.go | 40 +++++++++++++++++++--------------------- store/file/q1q4_file.go | 27 ++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/store/file/ods.go b/store/file/ods.go index 2d5703f413..30797f41fa 100644 --- a/store/file/ods.go +++ b/store/file/ods.go @@ -73,11 +73,19 @@ func CreateODSFile( return nil, fmt.Errorf("file create: %w", err) } - h, err := writeODSFile(f, eds, roots) + bufSize := int(eds.Width()) * share.Size + buf := bufio.NewWriterSize(f, bufSize) + + h, err := writeODSFile(buf, eds, roots) if err != nil { return nil, fmt.Errorf("writing ODS file: %w", err) } + err = buf.Flush() + if err != nil { + return nil, fmt.Errorf("flushing ODS file: %w", err) + } + err = f.Sync() if err != nil { return nil, fmt.Errorf("syncing file: %w", err) @@ -110,36 +118,26 @@ func writeODSFile(w io.Writer, eds *rsmt2d.ExtendedDataSquare, axisRoots *share. } // write quadrants - err = writeQuadrant(w, eds, int(h.shareSize), 0) + err = writeQ1(w, eds) if err != nil { return nil, fmt.Errorf("writing Q1: %w", err) } + return h, nil } -// writeQuadrant writes the quadrant of the square to the writer. it writes the quadrant in row-major -// order. It uses buffer to write the shares in bulk (row by row), which improves the write performance. -func writeQuadrant(w io.Writer, eds *rsmt2d.ExtendedDataSquare, shareSize, quadrantIdx int) error { - fromRow := quadrantIdx / 2 * int(eds.Width()) / 2 - toRow := fromRow + int(eds.Width())/2 - - fromCol := quadrantIdx % 2 * int(eds.Width()) / 2 - toCol := fromCol + int(eds.Width())/2 - - buf := bufio.NewWriterSize(w, shareSize*int(eds.Width())) - for rowIdx := fromRow; rowIdx < toRow; rowIdx++ { - for colIdx := fromCol; colIdx < toCol; colIdx++ { - share := eds.GetCell(uint(rowIdx), uint(colIdx)) - _, err := buf.Write(share) +// writeQ1 writes the first quadrant of the square to the writer. It writes the quadrant in row-major +// order +func writeQ1(w io.Writer, eds *rsmt2d.ExtendedDataSquare) error { + for i := range eds.Width() / 2 { + for j := range eds.Width() / 2 { + shr := eds.GetCell(i, j) // TODO: Avoid copying inside GetCell + _, err := w.Write(shr) if err != nil { - return fmt.Errorf("writing shares: %w", err) + return fmt.Errorf("writing share: %w", err) } } } - err := buf.Flush() - if err != nil { - return fmt.Errorf("flushing buffer: %w", err) - } return nil } diff --git a/store/file/q1q4_file.go b/store/file/q1q4_file.go index bb0ab49643..531aaebbf4 100644 --- a/store/file/q1q4_file.go +++ b/store/file/q1q4_file.go @@ -1,6 +1,7 @@ package file import ( + "bufio" "context" "fmt" "io" @@ -39,16 +40,40 @@ func CreateQ1Q4File(path string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDat return nil, err } - err = writeQuadrant(ods.fl, eds, int(ods.hdr.shareSize), 3) + bufSize := int(eds.Width()) * int(ods.hdr.shareSize) + buf := bufio.NewWriterSize(ods.fl, bufSize) + + err = writeQ4(buf, eds) if err != nil { return nil, fmt.Errorf("writing Q4: %w", err) } + err = buf.Flush() + if err != nil { + return nil, fmt.Errorf("flushing Q4: %w", err) + } + return &Q1Q4File{ ods: ods, }, nil } +// writeQ4 writes the frth quadrant of the square to the writer. iIt writes the quadrant in row-major +// order +func writeQ4(w io.Writer, eds *rsmt2d.ExtendedDataSquare) error { + half := eds.Width() / 2 + for i := range half { + for j := range half { + shr := eds.GetCell(i+half, j+half) // TODO: Avoid copying inside GetCell + _, err := w.Write(shr) + if err != nil { + return fmt.Errorf("writing share: %w", err) + } + } + } + return nil +} + func (f *Q1Q4File) Size(ctx context.Context) int { return f.ods.Size(ctx) } From eb663396fd57097275f2d7c730fe67b3f12355e4 Mon Sep 17 00:00:00 2001 From: Wondertan Date: Thu, 25 Jul 2024 01:55:43 +0200 Subject: [PATCH 2/6] sync q1q4 --- store/file/q1q4_file.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/store/file/q1q4_file.go b/store/file/q1q4_file.go index 531aaebbf4..7c5b7eeb45 100644 --- a/store/file/q1q4_file.go +++ b/store/file/q1q4_file.go @@ -53,6 +53,11 @@ func CreateQ1Q4File(path string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDat return nil, fmt.Errorf("flushing Q4: %w", err) } + err = ods.fl.Sync() + if err != nil { + return nil, fmt.Errorf("syncing file: %w", err) + } + return &Q1Q4File{ ods: ods, }, nil From ae370888d0ce07db31e5aaf4245da0bf43b50f36 Mon Sep 17 00:00:00 2001 From: Wondertan Date: Thu, 25 Jul 2024 17:12:47 +0200 Subject: [PATCH 3/6] write axis roots in place --- store/file/ods.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/store/file/ods.go b/store/file/ods.go index 30797f41fa..3fe61b96f2 100644 --- a/store/file/ods.go +++ b/store/file/ods.go @@ -141,16 +141,20 @@ func writeQ1(w io.Writer, eds *rsmt2d.ExtendedDataSquare) error { return nil } +// writeAxisRoots writes RowRoots followed by ColumnRoots. func writeAxisRoots(w io.Writer, roots *share.AxisRoots) error { - buf := make([]byte, 0, share.AxisRootSize*len(roots.RowRoots)) - for _, roots := range [][][]byte{roots.RowRoots, roots.ColumnRoots} { - for _, root := range roots { - buf = append(buf, root...) + for _, root := range roots.RowRoots { + if _, err := w.Write(root); err != nil { + return fmt.Errorf("writing columm roots: %w", err) } } - if _, err := w.Write(buf); err != nil { - return fmt.Errorf("writing axis roots: %w", err) + + for _, root := range roots.ColumnRoots { + if _, err := w.Write(root); err != nil { + return fmt.Errorf("writing columm roots: %w", err) + } } + return nil } From bd7ee986757e47c63eb23b30557dcb312406c9af Mon Sep 17 00:00:00 2001 From: Wondertan Date: Thu, 25 Jul 2024 17:44:28 +0200 Subject: [PATCH 4/6] use fixed buffer size --- store/file/ods.go | 6 ++++-- store/file/q1q4_file.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/store/file/ods.go b/store/file/ods.go index 3fe61b96f2..a1f689ce07 100644 --- a/store/file/ods.go +++ b/store/file/ods.go @@ -61,6 +61,8 @@ func OpenODSFile(path string) (*ODSFile, error) { }, nil } +const bufSize = 64 << 10 + // CreateODSFile creates a new file. File has to be closed after usage. func CreateODSFile( path string, @@ -73,7 +75,7 @@ func CreateODSFile( return nil, fmt.Errorf("file create: %w", err) } - bufSize := int(eds.Width()) * share.Size + // buffering gives us ~4x speed up buf := bufio.NewWriterSize(f, bufSize) h, err := writeODSFile(buf, eds, roots) @@ -145,7 +147,7 @@ func writeQ1(w io.Writer, eds *rsmt2d.ExtendedDataSquare) error { func writeAxisRoots(w io.Writer, roots *share.AxisRoots) error { for _, root := range roots.RowRoots { if _, err := w.Write(root); err != nil { - return fmt.Errorf("writing columm roots: %w", err) + return fmt.Errorf("writing row roots: %w", err) } } diff --git a/store/file/q1q4_file.go b/store/file/q1q4_file.go index 7c5b7eeb45..0f1cbddcae 100644 --- a/store/file/q1q4_file.go +++ b/store/file/q1q4_file.go @@ -40,7 +40,7 @@ func CreateQ1Q4File(path string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDat return nil, err } - bufSize := int(eds.Width()) * int(ods.hdr.shareSize) + // buffering gives us ~4x speed up buf := bufio.NewWriterSize(ods.fl, bufSize) err = writeQ4(buf, eds) From 3a90b77d4f06919b38ebf3227baa55d706b9d0cb Mon Sep 17 00:00:00 2001 From: Wondertan Date: Thu, 25 Jul 2024 17:53:09 +0200 Subject: [PATCH 5/6] improve comment --- store/file/ods.go | 8 +++++--- store/file/q1q4_file.go | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/store/file/ods.go b/store/file/ods.go index a1f689ce07..0cec409090 100644 --- a/store/file/ods.go +++ b/store/file/ods.go @@ -18,6 +18,10 @@ import ( var _ eds.AccessorStreamer = (*ODSFile)(nil) +// writeBufferSize defines buffer size for optimized batched writes into the file system. +// TODO(@Wondertan): Consider making it configurable and default being dynamic based on square size +const writeBufferSize = 64 << 10 + // ErrEmptyFile signals that the ODS file is empty. // This helps avoid storing empty block EDSes. var ErrEmptyFile = errors.New("file is empty") @@ -61,8 +65,6 @@ func OpenODSFile(path string) (*ODSFile, error) { }, nil } -const bufSize = 64 << 10 - // CreateODSFile creates a new file. File has to be closed after usage. func CreateODSFile( path string, @@ -76,7 +78,7 @@ func CreateODSFile( } // buffering gives us ~4x speed up - buf := bufio.NewWriterSize(f, bufSize) + buf := bufio.NewWriterSize(f, writeBufferSize) h, err := writeODSFile(buf, eds, roots) if err != nil { diff --git a/store/file/q1q4_file.go b/store/file/q1q4_file.go index 0f1cbddcae..d44d7b2ef2 100644 --- a/store/file/q1q4_file.go +++ b/store/file/q1q4_file.go @@ -41,7 +41,7 @@ func CreateQ1Q4File(path string, roots *share.AxisRoots, eds *rsmt2d.ExtendedDat } // buffering gives us ~4x speed up - buf := bufio.NewWriterSize(ods.fl, bufSize) + buf := bufio.NewWriterSize(ods.fl, writeBufferSize) err = writeQ4(buf, eds) if err != nil { From dd6edd25e7b3b10ad9244f423441549c6a099ab8 Mon Sep 17 00:00:00 2001 From: Wondertan Date: Thu, 25 Jul 2024 18:04:04 +0200 Subject: [PATCH 6/6] truncate todo --- store/file/ods.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/file/ods.go b/store/file/ods.go index 0cec409090..1e6af4af22 100644 --- a/store/file/ods.go +++ b/store/file/ods.go @@ -19,7 +19,7 @@ import ( var _ eds.AccessorStreamer = (*ODSFile)(nil) // writeBufferSize defines buffer size for optimized batched writes into the file system. -// TODO(@Wondertan): Consider making it configurable and default being dynamic based on square size +// TODO(@Wondertan): Consider making it configurable const writeBufferSize = 64 << 10 // ErrEmptyFile signals that the ODS file is empty.