You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@impala.apache.org by "Matthew Jacobs (Code Review)" <ge...@cloudera.org> on 2016/05/03 03:01:18 UTC
[Impala-CR](cdh5-trunk) IMPALA-3344: Simplify sorter and document/enforce invariants.
Matthew Jacobs has posted comments on this change.
Change subject: IMPALA-3344: Simplify sorter and document/enforce invariants.
......................................................................
Patch Set 5:
(42 comments)
~phew~ finally got through a first pass
http://gerrit.cloudera.org:8080/#/c/2826/5/be/src/runtime/sorter.cc
File be/src/runtime/sorter.cc:
Line 92: Finalize
How about FinalizeInput to make it clear it happens after consuming input but before sorting/merging? Before I read through all the implementations, I was confused about Finalize() vs PrepareRead(). It would be helpful to have a brief comment in the header walking through the expected calling pattern of these functions.
Line 116: .
, thus
I think joining these sentences will help make it clear this next sentence refers to the case where the run was unpinned
Line 116: 1 (2)
1 or 2 (if there are var-len slots)
Line 116: Atmost
At most
Line 116: (2)
?
Line 118: In either case, all rows in output_batch will have their fixed and var-len data from
: /// the same block.
This may be a bit misleading, should this be from the same 1 or 2 block(s), i.e. at most 1 fixed and 1 var-len block?
Line 135: //
///
Line 143: //
///
Line 147: // Finalize the list of blocks: update counters, delete empty final blocks, and
///
Line 157: var_slots
outdated
Line 169: Status
Can you comment on when is an error returned, since added indicates whether or not a block was allocated?
Line 190: /// data is in the next block.
, in which case tuple is unchanged.
Line 211: int
const
Line 214: int
same
Line 251: /// allocated block.
If it was needed, it is deleted in Finalize().
Line 254: so far
Does this include the # returned?
Line 285: TupleIterator
Run should be finalized and unsorted, right? Can you state those conditions and enforce with DCHECKs? I see you already have similar DCHECKs in TupleSorter::Sort(), but I think it's helpful to add them as well for readability & in case anything changes.
Line 290: 'run' and 'tuple_size' are passed as arguments to avoid
: /// the caller having to have redundant local variables with the same information
: /// when using multiple iterators.
I don't get what this is referring to
Line 305:
nit: remove line
Line 399: swap tuple
nit: swap_tuple
Line 610: UnpinAllBlocks
If an error is returned, this doesn't return all blocks. I don't know if it's worth addressing that in the code since I guess the error status gets propagated, should fail, and then gets cleaned up, but it would be helpful to indicate that behavior in the header comment.
Line 647: if (!added) {
: Status status = Status::MemLimitExceeded();
: status.AddDetail(Substitute(MEM_ALLOC_FAILED_ERROR_MSG, "variable"));
: return status;
: }
TryAddBlock says that calling with UNPIN_PREV should succeed. Is that wrong?
Line 739: if it did, we would have to return them to the caller.
Is it too tricky to add a DCHECK for this? I took a look and guess there's not a trivial way to do it with the way we handle non-partitioned agg/join mem.
Line 779: // GetNext() fills rows
More direct:
// Fills rows into ...
Line 803: ++var_len_blocks_index_;
: end_of_var_len_block_ = true;
I think it's a little confusing to increment the block_idx and also set a bool indicating the end of the block, because I'd assume end_of_var_len_block_ is referring to being at the end of the block at var_len_blocks_idx_, which is not the case. Would it be possible to increment the idx after PinNextReadBlock()? (That'd mean PinNextReadBlock's logic/math would shift a bit.)
Line 838: BufferedBlockMgr::Block* block = (*blocks)[block_index];
: BufferedBlockMgr::Block* prev_block = (*blocks)[block_index - 1];
If you make the change i mentioned on l804, this'd be next_block and the line below would be curr_block (or something like that), but then at least on l849 we'd have next_block->Pin(), which makes sense w/ the way this method is named.
Maybe this fn would take curr_block_idx and afterwards the caller increments the X_len_blocks_index_.
There are other ways to do this too, my original concern is just that it feels inconsistent right now between the indexing and what we consider to be current/next.
Line 859: for(const SlotDescriptor* string_slot: sort_tuple_desc_->string_slots()) {
nit missing space after for?
Line 945: DCHECK_EQ(block_offset, 0);
is this true because this is going to be the first string in the next block? I think that makes sense... Can you add a brief comment?
Line 956: May be NULL for
: // zero-length strings.
Please add a quick comment that this case comes from NULL+0=NULL, since NULL is just a typedef for 0. Also a DCHECK that we don't have NULL+x for any non-zero integer, e.g.
DHECK(block_start != NULL || block_offset == 0);
Line 976: index_(index),
: tuple_(NULL) {
nit: 1 line
Line 984: to
remove
Line 985: // block_index_ as if it pointing to the last tuple. Add tuple_size_ bytes to
it is
Line 987: int past_end_bytes = 0;
> This logic for initializing it past the end of the is hairy but it does wor
Maybe it's cleaner to have a static constructor that returns a TupleIterator at the end (e.g. end(), and prob a symmetrical begin()). That can call a private constructor that just sets the member vars but at least we don't have to handle this as a special case. I don't think we need any other public constructors, but if we do we can expose it easily.
Line 1043: temp_tuple_buffer_ = new uint8_t[tuple_size];
: swap_buffer_ = new uint8_t[tuple_size];
will your later work clean up these untracked allocations?
Line 1329: if
This num_free_buffers() as well as the available_allocated_buffers() (l1348) seems sketchy. I didn't understand why this would work if other operators could be consuming memory between here and l1348. Also there are warnings in buffered-block-mgr.h:392 indicating potential issues using these accessors. Is this something to clarify/address in your next patch?
Line 1387: Last
Why start calling this "Last"? At the time this is called it seems like it's more the current run.
Line 1389: sorted_data_size_->Add(unsorted_run_->TotalBytes());
can you move this down after the sort happens?
Line 1394: RETURN_IF_CANCELLED(state_);
maybe move this below this scoped block, and the counter incr form l1389 right above this
Line 1403: int max_runs_per_final_merge =
: block_mgr_->available_allocated_buffers() / blocks_per_run;
If I follow this (and the rest of the fn) correctly, sorting is more broken than I thought ... but I see you changed this in your next review so I'll skip over this for now.
Line 1416: sifficient
sufficient
http://gerrit.cloudera.org:8080/#/c/2826/5/tests/query_test/test_sort.py
File tests/query_test/test_sort.py:
Line 121: mean
means
Line 123: query
nit: line above this
--
To view, visit http://gerrit.cloudera.org:8080/2826
To unsubscribe, visit http://gerrit.cloudera.org:8080/settings
Gerrit-MessageType: comment
Gerrit-Change-Id: I9c619e81fd1b8ac50e257172c8bce101a112b52a
Gerrit-PatchSet: 5
Gerrit-Project: Impala
Gerrit-Branch: cdh5-trunk
Gerrit-Owner: Tim Armstrong <ta...@cloudera.com>
Gerrit-Reviewer: Dan Hecht <dh...@cloudera.com>
Gerrit-Reviewer: Matthew Jacobs <mj...@cloudera.com>
Gerrit-Reviewer: Tim Armstrong <ta...@cloudera.com>
Gerrit-HasComments: Yes