-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
executor: introduce Allocator
to manage memory used by executors
#10580
Conversation
/rebuild |
Here are the sysbench results. I have tested two rounds with The first round:
after:
avg(before) = 852.22, The second round:
after:
avg(before) = 846.166, |
/rebuild |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I suggest that this PR just add the library code and do not use it in the executor?
@@ -84,6 +84,7 @@ type baseExecutor struct { | |||
children []Executor | |||
retFieldTypes []*types.FieldType | |||
runtimeStats *execdetails.RuntimeStats | |||
allocator chunk.Allocator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we need to store it into executor? we can always get if from ctx.SessionVars()
@@ -908,6 +910,7 @@ func (e *SelectionExec) Open(ctx context.Context) error { | |||
|
|||
// Close implements plannercore.Plan Close interface. | |||
func (e *SelectionExec) Close() error { | |||
e.childResult.Release() | |||
e.childResult = nil | |||
e.selected = nil | |||
return e.baseExecutor.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about put the Release()
to the baseExecutor.Close()
?
It's easy to leak resource if we have to add Release()
in every executor...
@@ -0,0 +1,247 @@ | |||
package chunk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LICENSE
type Allocator interface { | ||
Alloc(l, c int) []byte | ||
Free(buf []byte) | ||
SetParent(a Allocator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of SetParent
?
|
||
// Free releases memory. | ||
func (b *multiBufChan) Free(buf []byte) { | ||
b.allocators[atomic.AddUint32(&b.freeIndex, 1)%b.numAllocators].Free(buf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The buffer from one allocator would be give back to another one?
pad = make([]byte, (1<<uint(capIndexBit))+1) | ||
) | ||
|
||
func getCapIndex(bitN uint) ([]int, []int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any comment to explain this function?
|
||
type bufChan struct { | ||
maxCap int | ||
bufList []chan []byte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This struct create too many small objects and bad for GC
// Close closes this allocator. | ||
func (b *bufChan) Close() { | ||
for _, ch := range b.bufList { | ||
for len(ch) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for _, buf := range ch {
...
}
for _, ch := range b.bufList { | ||
for len(ch) > 0 { | ||
buf := <-ch | ||
b.parent.Free(buf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will the buf
return to a Closed
allocator, taking concurrency into consideration?
col.elemBuf = nil | ||
} else { | ||
col.elemBuf = a.Alloc(elemLen, elemLen) | ||
col.data = a.Alloc(0, cap*elemLen) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During the usage of the chunk, the data/elemBuf/nullBitmap
will grow atomically and it's improper to return the memory to the allocator...
c.nullBitmap = append(c.nullBitmap, b)
Here is a post about how to recycle memory buffers in Golang. |
Close it because it increases the complexity of memory management and the 4.5% profit is not worth doing that. |
What problem does this PR solve?
The first step to fix #9396.
Introduce an
Allocator
to manage memory used by chunk in executors.What is changed and how it works?
Allocator
.Allocator
.Release
method toChunk
.Check List
Tests
Side effects