HappenLee commented on PR #70421:
URL: https://github.com/apache/doris/pull/70421#issuecomment-6788464100

   Potential performance regression: validation can clone columns that are 
already exclusively owned.
   
   The shared-column isolation is needed for correctness, but the new helper 
introduces two extra owners before the COW decision in 
`_internal_validate_column()`:
   
   ```cpp
   ColumnPtr real_column_ptr =
           column_ptr == nullptr ? column : column_ptr->get_nested_column_ptr();
   
   // Inside mutate_column():
   auto mutable_column = IColumn::mutate(column);
   ```
   
   For an initially exclusive, non-nullable column, the reference count is 1 in 
the owning slot, 2 after constructing `real_column_ptr`, and 3 when the lvalue 
`column` is copied into the by-value parameter of `IColumn::mutate(Ptr ptr)`. 
Consequently, `shallow_mutate()` takes the `clone()` branch even though there 
is no other consumer that needs the old column. The `ColumnPtr&` validation 
parameter itself does not add an owner.
   
   ```cpp
   static MutablePtr mutate(Ptr ptr) {
       MutablePtr res = ptr->shallow_mutate(); // Clone decision happens here.
       ptr.reset();                          // Too late to avoid that clone.
       res->mutate_subcolumns();
       return res;
   }
   ```
   
   For nullable columns, the local alias additionally retains the nested column 
while the by-value parameter retains the wrapper. Also, `mutate_subcolumns()` 
recursively detaches descendants. The ARRAY/MAP/STRUCT validation branches then 
recurse and repeat the same ownership pattern, potentially copying 
already-detached descendants again. Even read-only validation of an exclusively 
owned `ARRAY<INT>` enters `mutate_column()` eagerly.
   
   This can add column-data copying, allocation, memory bandwidth use, and peak 
memory, especially for large or deeply nested complex columns. Copying the 
smart pointers only increments reference counts; the subsequent COW calls cause 
the column clones.
   
   Suggested changes:
   
   1. Use non-owning, read-only views (for example `const IColumn*`) for 
`real_column_ptr`, and refresh them after replacing the owning column.
   2. Acquire mutable ownership by consuming the actual owner through the 
existing scoped ownership mechanism where applicable, preserving restoration on 
early returns and exceptions. Changing only the call to 
`IColumn::mutate(std::move(column))` is insufficient while the owning 
`real_column_ptr` alias is still alive.
   3. Once a column tree has been made mutable, avoid repeating whole-subtree 
COW at each recursive validation level, and defer mutation for paths that only 
need to inspect values. Keep the necessary COW isolation for genuinely shared 
parents and descendants.
   
   Please add ownership/allocation coverage alongside the SQL correctness 
regression: exclusive columns and descendants should be reused when possible; 
shared outputs must remain isolated during string truncation; nested 
ARRAY/MAP/STRUCT inputs should not repeatedly clone the same already-detached 
payload.
   
   Validation: traced the COW implementation and reproduced the extra-clone 
pattern in an isolated model using its reference-counting implementation. This 
is not a full BE benchmark, so no SQL slowdown or speedup percentage is claimed.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to