Coverage Report

Created: 2026-07-08 14:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/pipeline/task_queue.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "exec/pipeline/task_queue.h"
19
20
// IWYU pragma: no_include <bits/chrono.h>
21
#include <chrono> // IWYU pragma: keep
22
#include <memory>
23
#include <string>
24
#include <vector>
25
26
#include "common/logging.h"
27
#include "exec/pipeline/pipeline_task.h"
28
#include "runtime/workload_group/workload_group.h"
29
30
namespace doris {
31
32
5.95M
PipelineTaskSPtr SubTaskQueue::try_take(bool is_steal) {
33
5.95M
    if (_queue.empty()) {
34
0
        return nullptr;
35
0
    }
36
5.95M
    auto task = _queue.front();
37
5.95M
    _queue.pop();
38
5.95M
    return task;
39
5.95M
}
40
41
7.81k
std::queue<PipelineTaskSPtr> SubTaskQueue::take_all() {
42
7.81k
    std::queue<PipelineTaskSPtr> tasks;
43
7.81k
    tasks.swap(_queue);
44
7.81k
    return tasks;
45
7.81k
}
46
47
////////////////////  PriorityTaskQueue ////////////////////
48
49
3.33k
PriorityTaskQueue::PriorityTaskQueue() : _closed(false) {
50
3.33k
    double factor = 1;
51
23.3k
    for (int i = SUB_QUEUE_LEVEL - 1; i >= 0; i--) {
52
19.9k
        _sub_queues[i].set_level_factor(factor);
53
19.9k
        factor *= LEVEL_QUEUE_TIME_FACTOR;
54
19.9k
    }
55
3.33k
}
56
57
1.30k
void PriorityTaskQueue::close() {
58
1.30k
    std::vector<std::queue<PipelineTaskSPtr>> tasks_to_release;
59
1.30k
    {
60
1.30k
        std::unique_lock<std::mutex> lock(_work_size_mutex);
61
1.30k
        _closed = true;
62
1.30k
        const auto total_task_size = _total_task_size.exchange(0);
63
1.30k
        _wait_task.notify_all();
64
1.30k
        DorisMetrics::instance()->pipeline_task_queue_size->increment(
65
1.30k
                -static_cast<int64_t>(total_task_size));
66
7.81k
        for (auto& sub_queue : _sub_queues) {
67
7.81k
            tasks_to_release.emplace_back(sub_queue.take_all());
68
7.81k
        }
69
1.30k
    }
70
7.81k
    for (auto& tasks : tasks_to_release) {
71
7.81k
        while (!tasks.empty()) {
72
0
            tasks.front()->pop_out_runnable_queue();
73
0
            tasks.pop();
74
0
        }
75
7.81k
    }
76
1.30k
}
77
78
1.34G
PipelineTaskSPtr PriorityTaskQueue::_try_take_unprotected(bool is_steal) {
79
1.34G
    if (_total_task_size == 0 || _closed) {
80
1.34G
        return nullptr;
81
1.34G
    }
82
83
18.4E
    double min_vruntime = 0;
84
18.4E
    int level = -1;
85
18.4E
    for (int i = 0; i < SUB_QUEUE_LEVEL; ++i) {
86
35.7M
        double cur_queue_vruntime = _sub_queues[i].get_vruntime();
87
35.7M
        if (!_sub_queues[i].empty()) {
88
5.95M
            if (level == -1 || cur_queue_vruntime < min_vruntime) {
89
5.95M
                level = i;
90
5.95M
                min_vruntime = cur_queue_vruntime;
91
5.95M
            }
92
5.95M
        }
93
35.7M
    }
94
18.4E
    DCHECK(level != -1);
95
18.4E
    _queue_level_min_vruntime = uint64_t(min_vruntime);
96
97
18.4E
    auto task = _sub_queues[level].try_take(is_steal);
98
18.4E
    if (task) {
99
5.95M
        task->update_queue_level(level);
100
5.95M
        _total_task_size--;
101
5.95M
        DorisMetrics::instance()->pipeline_task_queue_size->increment(-1);
102
5.95M
    }
103
18.4E
    return task;
104
1.34G
}
105
106
5.95M
int PriorityTaskQueue::_compute_level(uint64_t runtime) {
107
18.4E
    for (int i = 0; i < SUB_QUEUE_LEVEL - 1; ++i) {
108
5.95M
        if (runtime <= _queue_level_limit[i]) {
109
5.95M
            return i;
110
5.95M
        }
111
5.95M
    }
112
18.4E
    return SUB_QUEUE_LEVEL - 1;
113
5.95M
}
114
115
1.32G
PipelineTaskSPtr PriorityTaskQueue::try_take(bool is_steal) {
116
    // TODO other efficient lock? e.g. if get lock fail, return null_ptr
117
1.32G
    std::unique_lock<std::mutex> lock(_work_size_mutex);
118
1.32G
    return _try_take_unprotected(is_steal);
119
1.32G
}
120
121
26.4M
PipelineTaskSPtr PriorityTaskQueue::take(uint32_t timeout_ms) {
122
26.4M
    std::unique_lock<std::mutex> lock(_work_size_mutex);
123
26.4M
    auto task = _try_take_unprotected(false);
124
26.4M
    if (task) {
125
5.95k
        return task;
126
26.4M
    } else {
127
26.4M
        if (timeout_ms > 0) {
128
26.4M
            _wait_task.wait_for(lock, std::chrono::milliseconds(timeout_ms));
129
18.4E
        } else {
130
18.4E
            _wait_task.wait(lock);
131
18.4E
        }
132
26.4M
        return _try_take_unprotected(false);
133
26.4M
    }
134
26.4M
}
135
136
5.95M
Status PriorityTaskQueue::push(PipelineTaskSPtr task) {
137
5.95M
    auto level = _compute_level(task->get_runtime_ns());
138
5.95M
    std::unique_lock<std::mutex> lock(_work_size_mutex);
139
5.95M
    if (_closed) {
140
0
        return Status::InternalError("WorkTaskQueue closed");
141
0
    }
142
143
    // update empty queue's  runtime, to avoid too high priority
144
5.95M
    if (_sub_queues[level].empty() &&
145
5.95M
        double(_queue_level_min_vruntime) > _sub_queues[level].get_vruntime()) {
146
0
        _sub_queues[level].adjust_runtime(_queue_level_min_vruntime);
147
0
    }
148
149
5.95M
    task->put_in_runnable_queue();
150
5.95M
    _sub_queues[level].push_back(task);
151
5.95M
    _total_task_size++;
152
5.95M
    DorisMetrics::instance()->pipeline_task_queue_size->increment(1);
153
5.95M
    _wait_task.notify_one();
154
5.95M
    return Status::OK();
155
5.95M
}
156
157
129
MultiCoreTaskQueue::~MultiCoreTaskQueue() = default;
158
159
MultiCoreTaskQueue::MultiCoreTaskQueue(int core_size)
160
195
        : _prio_task_queues(core_size), _closed(false), _core_size(core_size) {}
161
162
90
void MultiCoreTaskQueue::close() {
163
90
    if (_closed) {
164
0
        return;
165
0
    }
166
90
    _closed = true;
167
    // close all priority task queue
168
90
    std::ranges::for_each(_prio_task_queues,
169
1.30k
                          [](auto& prio_task_queue) { prio_task_queue.close(); });
170
90
}
171
172
14.2M
PipelineTaskSPtr MultiCoreTaskQueue::take(int core_id) {
173
14.2M
    PipelineTaskSPtr task = nullptr;
174
38.1M
    while (!_closed) {
175
18.4E
        DCHECK(_prio_task_queues.size() > core_id)
176
18.4E
                << " list size: " << _prio_task_queues.size() << " core_id: " << core_id
177
18.4E
                << " _core_size: " << _core_size << " _next_core: " << _next_core.load();
178
29.8M
        task = _prio_task_queues[core_id].try_take(false);
179
29.8M
        if (task) {
180
1.06M
            break;
181
1.06M
        }
182
28.7M
        task = _steal_take(core_id);
183
28.7M
        if (task) {
184
2.32M
            break;
185
2.32M
        }
186
26.4M
        task = _prio_task_queues[core_id].take(WAIT_CORE_TASK_TIMEOUT_MS /* timeout_ms */);
187
26.4M
        if (task) {
188
2.57M
            break;
189
2.57M
        }
190
26.4M
    }
191
14.2M
    if (task) {
192
5.95M
        task->pop_out_runnable_queue();
193
5.95M
    }
194
14.2M
    return task;
195
14.2M
}
196
197
28.7M
PipelineTaskSPtr MultiCoreTaskQueue::_steal_take(int core_id) {
198
28.7M
    DCHECK(core_id < _core_size);
199
28.7M
    int next_id = core_id;
200
1.33G
    for (int i = 1; i < _core_size; ++i) {
201
1.31G
        ++next_id;
202
1.31G
        if (next_id == _core_size) {
203
26.6M
            next_id = 0;
204
26.6M
        }
205
1.31G
        DCHECK(next_id < _core_size);
206
1.31G
        auto task = _prio_task_queues[next_id].try_take(true);
207
1.31G
        if (task) {
208
2.32M
            return task;
209
2.32M
        }
210
1.31G
    }
211
26.3M
    return nullptr;
212
28.7M
}
213
214
5.49M
Status MultiCoreTaskQueue::push_back(PipelineTaskSPtr task) {
215
5.49M
    int thread_id = task->get_thread_id(_core_size);
216
5.49M
    if (thread_id < 0) {
217
1.53M
        thread_id = _next_core.fetch_add(1) % _core_size;
218
1.53M
    }
219
5.49M
    return push_back(task, thread_id);
220
5.49M
}
221
222
5.95M
Status MultiCoreTaskQueue::push_back(PipelineTaskSPtr task, int core_id) {
223
5.95M
    DCHECK(core_id < _core_size);
224
5.95M
    return _prio_task_queues[core_id].push(task);
225
5.95M
}
226
227
5.49M
void MultiCoreTaskQueue::update_statistics(PipelineTask* task, int64_t time_spent) {
228
    // if the task not execute but exception early close, core_id == -1
229
    // should not do update_statistics
230
5.50M
    if (auto core_id = task->get_thread_id(_core_size); core_id >= 0) {
231
5.50M
        task->inc_runtime_ns(time_spent);
232
5.50M
        _prio_task_queues[core_id].inc_sub_queue_runtime(task->get_queue_level(), time_spent);
233
5.50M
    }
234
5.49M
}
235
236
} // namespace doris