Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance improvement #107

Merged
merged 16 commits into from
Feb 13, 2024
Merged
29 changes: 17 additions & 12 deletions internal/rle/rle.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import (

var encodedPool = sync.Pool{
New: func() interface{} {
return make([]uint8, 0, remarkable.ScreenHeight*remarkable.ScreenWidth)
return new(bytes.Buffer)
},
}

var bufferPool = sync.Pool{
New: func() any {
return make([]byte, 0, remarkable.ScreenHeight*remarkable.ScreenWidth*2)
},
}

Expand Down Expand Up @@ -38,26 +44,25 @@ func (rlewriter *RLE) Write(data []byte) (int, error) {
if length == 0 {
return 0, nil
}
encoded := encodedPool.Get().([]uint8) // Borrow a slice from the pool
defer encodedPool.Put(encoded)
buf := bufferPool.Get().([]uint8)
defer bufferPool.Put(buf)

current := data[0]
count := 0
count := uint8(0)

for _, datum := range data {
for i := 0; i < remarkable.ScreenWidth*remarkable.ScreenHeight; i++ {
datum := data[i*2]
if count < 254 && datum == current {
count++
} else {
encoded = append(encoded, uint8(count))
encoded = append(encoded, uint8(current))
buf = append(buf, count)
buf = append(buf, current)
current = datum
count = 1
}
}
buf = append(buf, count)
buf = append(buf, current)

encoded = append(encoded, uint8(count))
encoded = append(encoded, uint8(current))

n, err := io.Copy(rlewriter.sub, bytes.NewBuffer(encoded))
return int(n), err
return rlewriter.sub.Write(buf)
}
16 changes: 16 additions & 0 deletions internal/stream/bench_new
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
goos: linux
goarch: amd64
pkg: github.com/owulveryck/goMarkableStream/internal/stream
cpu: 11th Gen Intel(R) Core(TM) i3-1115G4 @ 3.00GHz
BenchmarkFetchAndSend-4 64 17426490 ns/op 5398064 B/op 0 allocs/op
BenchmarkFetchAndSend-4 75 19568566 ns/op 9248058 B/op 0 allocs/op
BenchmarkFetchAndSend-4 76 17417175 ns/op 9015990 B/op 0 allocs/op
BenchmarkFetchAndSend-4 74 17624161 ns/op 9401254 B/op 0 allocs/op
BenchmarkFetchAndSend-4 75 16933614 ns/op 9248051 B/op 0 allocs/op
BenchmarkFetchAndSend-4 73 18026694 ns/op 9530045 B/op 0 allocs/op
BenchmarkFetchAndSend-4 75 17434884 ns/op 9136203 B/op 0 allocs/op
BenchmarkFetchAndSend-4 75 17827753 ns/op 9164056 B/op 0 allocs/op
BenchmarkFetchAndSend-4 70 18889237 ns/op 9908626 B/op 0 allocs/op
BenchmarkFetchAndSend-4 70 18005032 ns/op 9908626 B/op 0 allocs/op
PASS
ok github.com/owulveryck/goMarkableStream/internal/stream 15.307s
16 changes: 16 additions & 0 deletions internal/stream/bench_new2
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
goos: linux
goarch: amd64
pkg: github.com/owulveryck/goMarkableStream/internal/stream
cpu: 11th Gen Intel(R) Core(TM) i3-1115G4 @ 3.00GHz
BenchmarkFetchAndSend-4 79 14389482 ns/op 11893927 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 15035708 ns/op 10108666 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 12881031 ns/op 10087776 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 13603009 ns/op 10087776 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 13045376 ns/op 10114073 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 13512878 ns/op 10087776 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 12939981 ns/op 10087776 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 13988847 ns/op 10087776 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 12817359 ns/op 10140369 B/op 2 allocs/op
BenchmarkFetchAndSend-4 100 13959212 ns/op 10087776 B/op 2 allocs/op
PASS
ok github.com/owulveryck/goMarkableStream/internal/stream 15.604s
16 changes: 16 additions & 0 deletions internal/stream/bench_new3
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
goos: linux
goarch: amd64
pkg: github.com/owulveryck/goMarkableStream/internal/stream
cpu: 11th Gen Intel(R) Core(TM) i3-1115G4 @ 3.00GHz
BenchmarkFetchAndSend-4 86 14446580 ns/op 7992410 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12809843 ns/op 6820878 B/op 1 allocs/op
BenchmarkFetchAndSend-4 97 12796436 ns/op 7053368 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12317484 ns/op 6820878 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 13181974 ns/op 6820878 B/op 1 allocs/op
BenchmarkFetchAndSend-4 97 12577923 ns/op 7031832 B/op 1 allocs/op
BenchmarkFetchAndSend-4 98 13408105 ns/op 6960079 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 13360119 ns/op 6926064 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12104609 ns/op 6820878 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12607977 ns/op 6820878 B/op 1 allocs/op
PASS
ok github.com/owulveryck/goMarkableStream/internal/stream 14.923s
16 changes: 16 additions & 0 deletions internal/stream/bench_new4
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
goos: linux
goarch: amd64
pkg: github.com/owulveryck/goMarkableStream/internal/stream
cpu: 11th Gen Intel(R) Core(TM) i3-1115G4 @ 3.00GHz
BenchmarkFetchAndSend-4 96 13647226 ns/op 7159864 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 13476577 ns/op 6873471 B/op 1 allocs/op
BenchmarkFetchAndSend-4 98 12466472 ns/op 7121077 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12856791 ns/op 6873471 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12318264 ns/op 6873471 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12955263 ns/op 6873471 B/op 1 allocs/op
BenchmarkFetchAndSend-4 93 12505268 ns/op 7526392 B/op 1 allocs/op
BenchmarkFetchAndSend-4 96 12427089 ns/op 7159864 B/op 1 allocs/op
BenchmarkFetchAndSend-4 98 13074079 ns/op 7013745 B/op 1 allocs/op
BenchmarkFetchAndSend-4 100 12493149 ns/op 6873471 B/op 1 allocs/op
PASS
ok github.com/owulveryck/goMarkableStream/internal/stream 15.636s
7 changes: 7 additions & 0 deletions internal/stream/bench_old
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
goos: linux
goarch: amd64
pkg: github.com/owulveryck/goMarkableStream/internal/stream
cpu: 11th Gen Intel(R) Core(TM) i3-1115G4 @ 3.00GHz
BenchmarkFetchAndSend-4 337 21152659 ns/op 11381657 B/op 4 allocs/op
PASS
ok github.com/owulveryck/goMarkableStream/internal/stream 14.407s
29 changes: 29 additions & 0 deletions internal/stream/benchfetchandsend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package stream

import (
"testing"

"github.com/owulveryck/goMarkableStream/internal/rle"
)

func BenchmarkFetchAndSend(b *testing.B) {
// Setup: Create a large enough mockReaderAt to test performance
width, height := 2872, 2404 // Example size; adjust based on your needs
mockReader := NewMockReaderAt(width, height) // Using the mock from the previous example

handler := StreamHandler{
file: mockReader,
pointerAddr: 0,
}

mockWriter := NewMockResponseWriter()

rleWriter := rle.NewRLE(mockWriter)

data := make([]byte, width*height) // Adjust based on your payload size

b.ResetTimer() // Start timing here
for i := 0; i < b.N; i++ {
handler.fetchAndSend(rleWriter, data)
}
}
21 changes: 21 additions & 0 deletions internal/stream/benchresult
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
goos: linux
goarch: amd64
pkg: github.com/owulveryck/goMarkableStream/internal/stream
cpu: 11th Gen Intel(R) Core(TM) i3-1115G4 @ 3.00GHz
│ /tmp/bench_old │ bench_new │
│ sec/op │ sec/op vs base │
FetchAndSend-4 21.15m ± ∞ ¹ 20.66m ± ∞ ¹ ~ (p=1.000 n=1) ²
¹ need >= 6 samples for confidence interval at level 0.95
² need >= 4 samples to detect a difference at alpha level 0.05

│ /tmp/bench_old │ bench_new │
│ B/op │ B/op vs base │
FetchAndSend-4 10.854Mi ± ∞ ¹ 8.746Mi ± ∞ ¹ ~ (p=1.000 n=1) ²
¹ need >= 6 samples for confidence interval at level 0.95
² need >= 4 samples to detect a difference at alpha level 0.05

│ /tmp/bench_old │ bench_new │
│ allocs/op │ allocs/op vs base │
FetchAndSend-4 4.000 ± ∞ ¹ 0.000 ± ∞ ¹ ~ (p=1.000 n=1) ²
¹ need >= 6 samples for confidence interval at level 0.95
² need >= 4 samples to detect a difference at alpha level 0.05
Binary file added internal/stream/cpu.out
Binary file not shown.
31 changes: 17 additions & 14 deletions internal/stream/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ func (h *StreamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer rawFrameBuffer.Put(rawData) // Return the slice to the pool when done
// the informations are int4, therefore store it in a uint8array to reduce data transfer
rleWriter := rle.NewRLE(w)
extractor := &oneOutOfTwo{rleWriter}
writing := true
stopWriting := time.NewTicker(2 * time.Second)
defer stopWriting.Stop()
Expand All @@ -102,20 +101,24 @@ func (h *StreamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
writing = false
case <-ticker.C:
if writing {
_, err := h.file.ReadAt(rawData, h.pointerAddr)
if err != nil {
log.Println(err)
return
}
_, err = extractor.Write(rawData)
if err != nil {
log.Println("Error in writing", err)
return
}
if w, ok := w.(http.Flusher); ok {
w.Flush()
}
h.fetchAndSend(rleWriter, rawData)
}
}
}
}

func (h *StreamHandler) fetchAndSend(w io.Writer, rawData []uint8) {
_, err := h.file.ReadAt(rawData, h.pointerAddr)
if err != nil {
log.Println(err)
return
}
_, err = w.Write(rawData)
if err != nil {
log.Println("Error in writing", err)
return
}
if w, ok := w.(http.Flusher); ok {
w.Flush()
}
}
34 changes: 34 additions & 0 deletions internal/stream/handler_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stream

import (
"bytes"
"io"
"math/rand"
"net/http"
Expand Down Expand Up @@ -75,3 +76,36 @@ func TestStreamHandlerRaceCondition(t *testing.T) {
<-doneChan
}
}

func TestStreamHandler_fetchAndSend(t *testing.T) {
type fields struct {
file io.ReaderAt
pointerAddr int64
inputEventsBus *pubsub.PubSub
}
type args struct {
rawData []uint8
}
tests := []struct {
name string
fields fields
args args
wantW string
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := &StreamHandler{
file: tt.fields.file,
pointerAddr: tt.fields.pointerAddr,
inputEventsBus: tt.fields.inputEventsBus,
}
w := &bytes.Buffer{}
h.fetchAndSend(w, tt.args.rawData)
if gotW := w.String(); gotW != tt.wantW {
t.Errorf("StreamHandler.fetchAndSend() = %v, want %v", gotW, tt.wantW)
}
})
}
}
78 changes: 78 additions & 0 deletions internal/stream/mockreaderat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package stream

import (
"bytes"
"io"
"math/rand"
"net/http"
)

// MockReaderAt implements the io.ReaderAt interface.
type MockReaderAt struct {
width int
height int
data []byte
}

// NewMockReaderAt creates a new MockReaderAt with the specified dimensions and initializes its data.
func NewMockReaderAt(width, height int) *MockReaderAt {
size := width * height
data := make([]byte, size)

// Fill the slice with values where 70% are 0s and the rest are between 1 and 17.
for i := 0; i < size; i++ {
if rand.Float64() > 0.7 {
data[i] = byte(rand.Intn(17) + 1)
}
}

return &MockReaderAt{
width: width,
height: height,
data: data,
}
}

// ReadAt reads len(p) bytes into p starting at offset off in the mock data.
// It implements the io.ReaderAt interface.
func (m *MockReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
if off >= int64(len(m.data)) {
return 0, io.EOF
}

n = copy(p, m.data[off:])
if n < len(p) {
err = io.EOF
}

return n, err
}

// mockResponseWriter simulates an http.ResponseWriter for benchmarking purposes.
type mockResponseWriter struct {
headerMap http.Header
body *bytes.Buffer
}

func NewMockResponseWriter() *mockResponseWriter {
return &mockResponseWriter{
headerMap: make(http.Header),
body: new(bytes.Buffer),
}
}

func (m *mockResponseWriter) Header() http.Header {
return m.headerMap
}

func (m *mockResponseWriter) Write(data []byte) (int, error) {
return m.body.Write(data)
}

func (m *mockResponseWriter) WriteHeader(statusCode int) {
// For benchmarking, we might not need to simulate the status code.
}

func (m *mockResponseWriter) Flush() {
// Simulate flushing the buffer if implementing http.Flusher.
}
11 changes: 8 additions & 3 deletions internal/stream/oneoftwo.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package stream

import (
"encoding/binary"
"io"
"sync"

Expand All @@ -21,8 +20,14 @@ type oneOutOfTwo struct {
func (oneoutoftwo *oneOutOfTwo) Write(src []byte) (n int, err error) {
imageData := imagePool.Get().([]uint8)
defer imagePool.Put(imageData) // Return the slice to the pool when done
for i := 0; i < remarkable.ScreenHeight*remarkable.ScreenWidth; i++ {
imageData[i] = uint8(binary.LittleEndian.Uint16(src[i*2 : i*2+2]))
for i := range imageData {
imageData[i] = src[i*2] // Directly take the lower byte of each uint16 from src
}

/*
for i := 0; i < remarkable.ScreenHeight*remarkable.ScreenWidth; i++ {
imageData[i] = uint8(binary.LittleEndian.Uint16(src[i*2 : i*2+2]))
}
*/
return oneoutoftwo.w.Write(imageData)
}
Binary file added internal/stream/stream.test
Binary file not shown.
Loading