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

executor: decrease the memory usage of hashTable in HashJoinExec #11832

Merged
merged 12 commits into from Aug 29, 2019

Conversation

SunRunAway
Copy link
Contributor

@SunRunAway SunRunAway commented Aug 22, 2019

What problem does this PR solve?

part of #11607

What is changed and how it works?

See section 1 of #11607 (comment)

See benchmark at #11832 (comment)

Check List

Tests

  • Unit test
  • Integration test

Code changes

  • Has exported function/method change

Side effects

  • Possible performance regression

Related changes

  • None

@XuHuaiyu
Copy link
Contributor

Do we have any memory benchmark for this?

@codecov
Copy link

codecov bot commented Aug 22, 2019

Codecov Report

Merging #11832 into master will not change coverage.
The diff coverage is n/a.

@@             Coverage Diff             @@
##             master     #11832   +/-   ##
===========================================
  Coverage   81.4279%   81.4279%           
===========================================
  Files           442        442           
  Lines         95186      95186           
===========================================
  Hits          77508      77508           
  Misses        12216      12216           
  Partials       5462       5462

@qw4990 qw4990 self-requested a review August 22, 2019 09:16
@SunRunAway
Copy link
Contributor Author

/run-all-tests

@SunRunAway
Copy link
Contributor Author

SunRunAway commented Aug 22, 2019

benchmark result

TLDR

This pr has little improvement with time and great improvement with memory if join keys are larger enough, while has a little performance regression if join keys are simple.

go test -run=XX -bench="BenchmarkBuildHashTableForList|BenchmarkHashJoinExec" -test.benchmem -count 10
&&
benchstat /tmp/old.txt /tmp/new.txt

name                                                                     old time/op    new time/op    delta
HashJoinExec/(rows:100000,_concurency:4,_joinKeyIdx:_[0_1])-12              917ms ± 3%     735ms ± 3%  -19.81%  (p=0.000 n=8+10)
HashJoinExec/(rows:100000,_concurency:4,_joinKeyIdx:_[0])-12                168ms ± 2%     163ms ± 1%   -3.05%  (p=0.000 n=10+10)
BuildHashTableForList/(rows:100000,_concurency:4,_joinKeyIdx:_[0_1])-12     664ms ±11%     519ms ± 2%  -21.78%  (p=0.000 n=10+10)
BuildHashTableForList/(rows:100000,_concurency:4,_joinKeyIdx:_[0])-12      15.5ms ±12%    14.0ms ±12%   -9.64%  (p=0.009 n=10+10)

name                                                                     old alloc/op   new alloc/op   delta
HashJoinExec/(rows:100000,_concurency:4,_joinKeyIdx:_[0_1])-12              853MB ± 0%     305MB ± 0%  -64.24%  (p=0.000 n=10+10)
HashJoinExec/(rows:100000,_concurency:4,_joinKeyIdx:_[0])-12                307MB ± 0%     305MB ± 0%   -0.56%  (p=0.000 n=9+10)
BuildHashTableForList/(rows:100000,_concurency:4,_joinKeyIdx:_[0_1])-12     556MB ± 0%       8MB ± 0%  -98.57%  (p=0.000 n=10+10)
BuildHashTableForList/(rows:100000,_concurency:4,_joinKeyIdx:_[0])-12      10.4MB ± 0%     8.0MB ± 0%  -23.63%  (p=0.000 n=10+10)

name                                                                     old allocs/op  new allocs/op  delta
HashJoinExec/(rows:100000,_concurency:4,_joinKeyIdx:_[0_1])-12               215k ± 0%      306k ± 0%  +42.68%  (p=0.000 n=8+9)
HashJoinExec/(rows:100000,_concurency:4,_joinKeyIdx:_[0])-12                 206k ± 0%      306k ± 0%  +48.42%  (p=0.000 n=10+10)
BuildHashTableForList/(rows:100000,_concurency:4,_joinKeyIdx:_[0_1])-12     12.4k ± 0%      4.0k ± 1%  -67.38%  (p=0.000 n=10+10)
BuildHashTableForList/(rows:100000,_concurency:4,_joinKeyIdx:_[0])-12       4.08k ± 0%     4.04k ± 0%   -0.92%  (p=0.000 n=9+10)

old.txt
new.txt

@SunRunAway
Copy link
Contributor Author

/run-all-tests

@SunRunAway
Copy link
Contributor Author

/run-all-tests

@XuHuaiyu
Copy link
Contributor

executor/index_lookup_join.go
102:    lookupMap         *mvmap.MVMap
368:            lookupMap:         mvmap.NewMVMap(),

expression/aggregation/util.go
26:     existingKeys *mvmap.MVMap
35:             existingKeys: mvmap.NewMVMap(),

It seems that these two usages can be optimized, too. Maybe we can create an issue for it?

@SunRunAway
Copy link
Contributor Author

@XuHuaiyu
Yes, these are the other tasks of issue #11607

executor/join.go Outdated
@@ -50,7 +50,7 @@ type HashJoinExec struct {

// concurrency is the number of partition, build and join workers.
concurrency uint
hashTable *mvmap.MVMap
hashTable rowHashTable
innerFinished chan error
hashJoinBuffers []*hashJoinBuffer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this field can be removed.

executor/join.go Outdated
}

func (e *HashJoinExec) matchJoinKey(inner, outer chunk.Row) bool {
innerAllTypes := retTypes(e.innerExec)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my part, innerAllTypes, outerAllTypes := xxx, innerIdx, outerIdx := xxx, innerTp, outerTp := xxx are more clearer?

elemLen := len(c.elemBuf)
data = c.data[rowID*elemLen : rowID*elemLen+elemLen]
} else {
start, end := c.offsets[rowID], c.offsets[rowID]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it seem to be wrong that start == end, any test case covered this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benchmark was fake because of this bug. 😢
I'll update a new one.

return nil, errors.Trace(err)
flag := varintFlag
if mysql.HasUnsignedFlag(allTypes[i].Flag) {
if integer := row.GetInt64(i); integer < 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if integer := row.GetInt64(i); integer < 0 {
if row.GetInt64(i) < 0 {

default:
return nil, errors.Errorf("unsupport column type for encode %d", allTypes[i].Tp)
_, err = row.WriteTo(i, w)
Copy link
Contributor

@qw4990 qw4990 Aug 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's dangerous to skip the type interpretation and hash the raw data directly in the table building stage.
For example:

create table t1(x float);
create table t2(x double);
insert into t1 values (1);
insert into t2 values (1);
select * from t1, t2 where t1.x=t2.x;

An empty set is returned in this PR since it doesn't take data types into account when building the inner hash table.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since that, I've no good solution except changing all encodeXXX function along with an io.Writer argument to avoid extra memory copy.
I'll revert this part code for now and try to optimize it in further PR.
Any suggestion is very welcome, @qw4990 @zz-jason @XuHuaiyu

@SunRunAway SunRunAway force-pushed the disk-join-issue11607 branch 3 times, most recently from 4891038 to afe7881 Compare August 23, 2019 14:43
@SunRunAway
Copy link
Contributor Author

/run-all-tests

@SunRunAway
Copy link
Contributor Author

SunRunAway commented Aug 23, 2019

@XuHuaiyu @qw4990
All comments are addressed and the benchmark is updated, PTAL again, thanks.

@qw4990 qw4990 added status/can-merge Indicates a PR has been approved by a committer. status/LGT2 Indicates that a PR has LGTM 2. and removed status/can-merge Indicates a PR has been approved by a committer. labels Aug 28, 2019
@sre-bot
Copy link
Contributor

sre-bot commented Aug 28, 2019

/run-all-tests

@SunRunAway
Copy link
Contributor Author

@XuHuaiyu got two LGTMs, PTAL again~

}

// Put puts the key/rowPtr pairs to the rowHashMap, multiple rowPtrs are stored in a list.
func (m *rowHashMap) Put(hashKey uint64, rowPtr chunk.RowPtr) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the origin rows are all stored in a Chunk but not a List, we need to pass chunk.RowPtr{0, rowIdx} into this function?

@SunRunAway
Copy link
Contributor Author

SunRunAway commented Aug 29, 2019 via email

Copy link
Contributor

@XuHuaiyu XuHuaiyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@XuHuaiyu XuHuaiyu added the status/can-merge Indicates a PR has been approved by a committer. label Aug 29, 2019
@sre-bot
Copy link
Contributor

sre-bot commented Aug 29, 2019

/run-all-tests

@sre-bot
Copy link
Contributor

sre-bot commented Aug 29, 2019

@SunRunAway merge failed.

@SunRunAway
Copy link
Contributor Author

/run-common-test

@SunRunAway SunRunAway merged commit bdbaeb4 into pingcap:master Aug 29, 2019
@SunRunAway SunRunAway deleted the disk-join-issue11607 branch August 29, 2019 06:46
@SunRunAway
Copy link
Contributor Author

/bench

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sig/execution SIG execution status/can-merge Indicates a PR has been approved by a committer. status/LGT2 Indicates that a PR has LGTM 2. type/enhancement type/new-feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants