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

perf: nocopy read for ReadString and ReadBinary API if possible #315

Merged
merged 7 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 54 additions & 8 deletions nocopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ package netpoll

import (
"io"
"reflect"
"unsafe"

"github.com/bytedance/gopkg/lang/mcache"
)

// Reader is a collection of operations for nocopy reads.
Expand Down Expand Up @@ -108,9 +112,9 @@ type Reader interface {
// The usage of the design is a two-step operation, first apply for a section of memory,
// fill it and then submit. E.g:
//
// var buf, _ = Malloc(n)
// buf = append(buf[:0], ...)
// Flush()
// var buf, _ = Malloc(n)
// buf = append(buf[:0], ...)
// Flush()
//
// Note that it is not recommended to submit self-managed buffers to Writer.
// Since the writer is processed asynchronously, if the self-managed buffer is used and recycled after submission,
Expand Down Expand Up @@ -244,10 +248,52 @@ func NewIOReadWriter(rw ReadWriter) io.ReadWriter {
}

const (
block1k = 1 * 1024
block2k = 2 * 1024
block4k = 4 * 1024
block8k = 8 * 1024
block1k = 1 * 1024
block2k = 2 * 1024
block4k = 4 * 1024
block8k = 8 * 1024
block32k = 32 * 1024

pagesize = block8k
mallocMax = block8k * block1k // mallocMax is 8MB

minReuseBytes = 64 // only reuse bytes if n >= minReuseBytes

defaultLinkBufferMode = 0
// readonly mode indicate that the buffer node memory is not controlled by itself,
// so we cannot reuse the buffer or nocopy read it, default value is false.
readonlyMask uint8 = 1 << 0 // 0000 0001
// nocopyRead mode indicate that the buffer node has been no copy read and cannot reuse the buffer, default value is false.
nocopyReadMask uint8 = 1 << 1 // 0000 0010
)

const pagesize = block8k
// zero-copy slice convert to string
func unsafeSliceToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}

// zero-copy slice convert to string
func unsafeStringToSlice(s string) (b []byte) {
p := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
hdr.Data = uintptr(p)
hdr.Cap = len(s)
hdr.Len = len(s)
return b
}

// malloc limits the cap of the buffer from mcache.
func malloc(size, capacity int) []byte {
if capacity > mallocMax {
return make([]byte, size, capacity)
}
return mcache.Malloc(size, capacity)
}

// free limits the cap of the buffer from mcache.
func free(buf []byte) {
if cap(buf) > mallocMax {
return
}
mcache.Free(buf)
}
Loading
Loading