Files
doris/be/test/exprs/aggregate/agg_count_test.cpp
T
zclllyybb 5d97b29034 [improvement](be) Optimize count on nullable column (#64166)
Count aggregation without GROUP BY reaches
AggFnEvaluator::execute_single_add(), which calls
add_batch_single_place(). AggregateFunctionCount and
AggregateFunctionCountNotNullUnary previously inherited the row-by-row
helper there, so count(*) and count(nullable_expr) paid per-row
add/is_null_at costs even when all rows were aggregated into one state.

This patch adds batch implementations: count(*) increments the state
once by batch_size, while unary count(nullable_expr) checks the nullable
null map once and fast-paths the no-NULL case to count += batch_size.
When NULLs exist it uses simd::count_zero_num() over the null map to
count non-NULL rows. The nullable class name is kept because SQL
count(expr) counts non-NULL values, not NULL values.

Performance:
test with sql
```sql
select count(nullable(number)) from numbers("number"="1000000000");

select count(nullable(if(number >= 0, null, number))) from numbers("number"="1000000000");

select count(nullable(if(number % 2 = 0, number, null))) from numbers("number"="1000000000");
```
get result
```
 Scenario     before median / mean    after median / mean    median diff
━━━━━━━━━━━  ━━━━━━━━━━━━━━━━━━━━━━  ━━━━━━━━━━━━━━━━━━━━━  ━━━━━━━━━━━━━
 non NULL           645 / 648.6 ms         555 / 556.4 ms         -14.0%
───────────  ──────────────────────  ─────────────────────  ─────────────
 all NULL         1541 / 1539.6 ms       1448 / 1450.6 ms          -6.0%
───────────  ──────────────────────  ─────────────────────  ─────────────
 half NULL        4256 / 4261.2 ms       4192 / 4232.2 ms          -1.5%
```
2026-06-08 11:52:56 +08:00

56 lines
2.4 KiB
C++

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include <gtest/gtest.h>
#include "core/data_type/data_type_nullable.h"
#include "core/data_type/data_type_number.h"
#include "exprs/aggregate/agg_function_test.h"
namespace doris {
struct AggregateFunctionCountTest : public AggregateFunctiontest {};
TEST_F(AggregateFunctionCountTest, test_int64) {
create_agg("count", false, {std::make_shared<DataTypeInt64>()},
std::make_shared<DataTypeInt64>());
execute(Block({ColumnHelper::create_column_with_name<DataTypeInt64>({1, 2, 3})}),
ColumnHelper::create_column_with_name<DataTypeInt64>({3}));
}
TEST_F(AggregateFunctionCountTest, test_nullable_int64_without_null) {
create_agg("count", false,
{std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt64>())},
std::make_shared<DataTypeInt64>());
execute(Block({ColumnHelper::create_nullable_column_with_name<DataTypeInt64>({1, 2, 3, 4},
{0, 0, 0, 0})}),
ColumnHelper::create_column_with_name<DataTypeInt64>({4}));
}
TEST_F(AggregateFunctionCountTest, test_nullable_int64_with_null) {
create_agg("count", false,
{std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt64>())},
std::make_shared<DataTypeInt64>());
execute(Block({ColumnHelper::create_nullable_column_with_name<DataTypeInt64>({1, 2, 3, 4, 5},
{0, 1, 0, 1, 0})}),
ColumnHelper::create_column_with_name<DataTypeInt64>({3}));
}
} // namespace doris