
主频停滞,核心爆发:单个核心的主频已在 3-5GHz 徘徊多年,性能提升转而依赖核心数量(从个位数到百核级)和单核心的并行宽度(SIMD)。 内存墙加剧:CPU 与主存的速度差距已超 300 倍。缓存命中率成为性能的生命线,一次缓存未命中(Cache Miss)的代价足以执行数百条指令。 SIMD 成为标配:AVX2(256 位)、AVX-512(512 位)及 ARM SVE 等指令集,允许单条指令处理 4 至 16 个数据单元。不用 SIMD,就等于主动浪费超过 80%的浮点算力。
激进的内联与向量化:现代编译器(如 Clang/GCC)能在编译期进行循环展开、分支消除、自动向量化,但其优化能力极度依赖代码模式。虚函数、指针别名、分支预测失败会瞬间阻断其优化通路。 跨平台抽象:通过优良的代码模式,编译器能够自动为循环生成 SIMD 代码,无缝迁移不同平台。但对于复杂的数据处理逻辑,仍然需要手动生成 SIMD 代码。
火山模型:每行的虚函数调用,导致指令缓存(I-Cache) 被频繁冲刷,核心处于“饥渴”状态。 动态分支:在数据密集的循环中,一次预测失败可能清空长达 15-20 级的指令流水线。 随机内存访问:不连续的内存访问模式,让 CPU 的预取器(Prefetcher)失效,大部分时间在等待数据从内存加载。
指令乱序发射与流水线:现代 CPU 的流水线深度可达 15-20 级,并通过乱序执行引擎动态调度指令。互相没有依赖的指令虽然在汇编与机器码中有先后顺序,但实际可以完全并行。其性能发挥的关键在于指令流的连续性和可预测性。一次错误的分支预测会导致整个流水线被重置,带来约 15 个时钟周期的惩罚;而一次缓存未命中(Cache Miss) 导致的数百周期等待,更会让所有精巧设计瞬间归零。[1] 微指令缓存(μop Cache):μop Cache 是处理器前端的一种硬件缓存结构,能够缓存热指令,跳过解码阶段,提升每周期指令数(IPC)。根据常规统计,μop Cache 能够产生稳定、可观(2% ~ 10%)的 IPC 增加。但在目前常见的数据应用中,实际命中率从 30% 到 70% 差距很大[2],是明显的性能提升点。 向量指令集(SIMD):这是性能提升一个数量级的核心武器。从 SSE 的 128 位到 AVX2 的 256 位,再到 AVX-512 的 512 位,意味着单条指令可同时处理的数据量从 4 个 32 位整数跃升至 16 个。理论峰值算力因此呈倍数增长。例如,在理想的数据密集循环中,使用 AVX-512 相比标量代码可带来 10 倍以上的吞吐量提升。


// 核心逻辑示意
template <typename T>
class ColumnVector final : public IColumn {
private:
PaddedPODArray 保证了内存对齐,通常按 64 字节对齐以适配 Cache Line
PaddedPODArray<T> data;
public:
向量化计算入口,不再处理单行,而是处理整个 data 数组
void filter(const Filter& filt) override {
...
}
}

自动向量化:对于绝大多数情况下,这是现代编译器提供给我们的最佳选择。核心在于,简化循环体,抽离控制流分支(Branch),让编译器识别出 Auto Vectorization 的机会。 手动向量化:对于实际汇编识别出未能正常 Auto Vectorization 的算子,或是那些热点路径上的向量化需求,Doris 工程师并不避讳直接手写 Intrinsic 代码。相比于自动生成的代码,此种方式往往拥有更加极致的性能,几乎没有指令浪费。
Volnitsky 算法:在子串查找中,利用 SIMD 并行比较多个字符,快速跳过不匹配区域。 SimdJson:在解析 JSON Path 时,利用 SIMD 指令快速定位结构符(如 {, }, :),大幅缩短解析路径。
// SIMD 子串匹配
const uint8_t* _search(const uint8_t* haystack, const uint8_t* haystack_end) const {
......
while (haystack < haystack_end && haystack_end - haystack >= needle_size) {
#if defined(__SSE4_1__) || defined(__aarch64__)
if ((haystack + 1 + n) <= haystack_end && page_safe(haystack)) {
/// find first and second characters
const auto v_haystack_block_first =
_mm_loadu_si128(reinterpret_cast<const __m128i*>(haystack));
const auto v_haystack_block_second =
_mm_loadu_si128(reinterpret_cast<const __m128i*>(haystack + 1));
const auto v_against_pattern_first =
_mm_cmpeq_epi8(v_haystack_block_first, first_pattern);
const auto v_against_pattern_second =
_mm_cmpeq_epi8(v_haystack_block_second, second_pattern);
const auto mask = _mm_movemask_epi8(
_mm_and_si128(v_against_pattern_first, v_against_pattern_second));
/// first and second characters not present in 16 octets starting at `haystack`
if (mask == 0) {
haystack += n;
continue;
}
......
}
class VirtualBase {
virtual int foo(int x);
};
class VirtualDerived :public VirtualBase {
int foo(int x) override;
};
class NonVirtual {
int bar(int x);
};
static void BM_VirtualCall(benchmark::State& state) {
VirtualBase* obj = new VirtualDerived();
for (auto _ : state) {
result = obj->foo(42);
}
}
static void BM_NonVirtualCall(benchmark::State& state) {
NonVirtualBase obj;
for (auto _ : state) {
result = obj.bar(42);
}
}
static void BM_DirectCall(benchmark::State& state) {
VirtualDerived obj;
for (auto _ : state) {
result = obj.foo(42);
}
}

template <PrimitiveType PType>
struct AddDaysImpl {
......
static inline ReturnNativeType execute(const InputNativeType& t, IntervalNativeType delta) {
// PType 已经固定,不需要运行期判断
return date_time_add<TimeUnit::DAY, PType, IntervalNativeType>(t, delta);
// compare to
// if (t.is_date) {
// return date_time_add<TimeUnit::DAY, DATEV2, IntervalNativeType>(t, delta);
// } else {
// return date_time_add<TimeUnit::DAY, DATETIMEV2, IntervalNativeType>(t, delta);
// }
}
// 不同模板实例参数不同,函数匹配时直接命中对应实例
static DataTypes get_variadic_argument_types() {
return {std ::make_shared<typename PrimitiveTypeTraits<PType>::DataType>(),
std ::make_shared<typename PrimitiveTypeTraits<IntervalPType>::DataType>()};
}
}
using FunctionAddDays = FunctionDateOrDateTimeComputation<AddDaysImpl<TYPE_DATEV2>>;
using FunctionDatetimeAddDays = FunctionDateOrDateTimeComputation<AddDaysImpl<TYPE_DATETIMEV2>>;
factory.register_function<FunctionDatetimeAddDays>();
factory.register_function<FunctionAddDays>();

每次都走系统分配器,锁竞争严重; 碎片多,RSS(常驻内存集,Resident Set Size)难以控制。
无锁分配:算子内部申请内存通常只是当前线程缓存内的指针简单移动(Bump Pointer),完全无锁。 批量释放:查询结束后,整块 Arena 统一释放,避免了数百万次小对象的析构开销。 Cache 友好:同一算子使用的对象在内存中紧凑排列,极大提升了 CPU 缓存命中率。
class Arena : private boost::noncopyable {
struct Chunk :private Allocator<false> {
......
}
public:
char* alloc(size_t size) {
_init_head_if_needed();
if (UNLIKELY(head->pos + size > head->end)) {
_add_chunk(size);
}
// 直接 bump pointer,开销无限小
char* res = head->pos;
head->pos += size;
return res;
}
// 一次性统一回收
void clear(bool delete_head = false) {
......
}
};

如果一个线程因为网络或磁盘 IO 阻塞,操作系统就会进行线程的上下文切换(Context Switch),开销在微秒级别。随着查询数量和规模增长,系统线程数暴涨,导致上下文切换频繁,overhead 明显增加; 无法细粒度实现 query 之间的公平调度。大小查询混合场景下,小查询被调度到的机会明显下降,延迟大幅增高; 线程频繁迁移,丧失 NUMA 和 Cache 亲和性,影响查询性能; 依赖底层数据分布,无法交换数据,数据倾斜对性能影响巨大 ……

就绪队列(Runnable Queue):一旦数据就绪,依赖被满足,Task 转移至就绪队列。可以随时被调度执行。 阻塞队列(Blocked Queue):当 Task 需要等待 IO 或 RPC 数据时,它被放入阻塞队列,不占用操作系统线程。 执行线程池:一组固定数量的线程不断从就绪队列取出 Task 执行。执行线程绑核以保证 Cache 命中率。




相比于 Clickhouse,Doris 在其自家维护的 ClickBench 上曾多次取得领先,上一次提交的成绩位列第 2 名,领先于 Clickhouse 的第三名 2% 的总分。在 SSB、TPC-H 上,更是分别有 3 倍和 60 倍的性能领先。在 TPC-DS 上,Clickhouse 在同等资源下只能执行约 50% 的查询,这部分成绩比 Doris 的总成绩还落后 1 倍[15]。 而在实时更新场景中,二者差距更大。根据发行商 VeloDB 的测试结果,在比 Clickhouse 更差的硬件条件下,25% 更新率场景下 Doris 比 Clickhouse 快 14 倍;100% 更新率时领先更是达到了 18 倍[16]。 相比于 Trino/Presto,Doris 在 TPC-DS 1TB 测试中使用同等条件进行数据湖查询,达到了 3 倍的性能领先;使用 Doris 内表性能领先更是达到 10 倍之多。在实际用户场景中,查询延时更是降低了最多 20 倍[17]。 与 Spark 对比,Doris 在其擅长的复杂查询下性能领先 4-6 倍,实时场景下更是实现了代际级别的延迟优势[13]。 对比擅长半结构化数据存储的 ElasticSearch,Doris 在半结构化测试集 JsonBench 上达到了 2 倍性能领先,同时超越了 Clickhouse。相比于 Postgresql 领先幅度更是达到 80 倍之多[18]。
针对计算效率,我们通过全面的向量化重构和模板化编程,将处理单元从“行”升级为“列”,并在编译期固化类型与分支,让生成的代码近乎直接匹配 CPU 的高效流水线。 针对内存效率,我们引入专用的内存分配器与池化技术,大幅削减高并发下的锁竞争与碎片,确保数据在缓存中紧凑排列。 针对多核调度,我们自研 Pipeline 执行引擎,在用户态实现精细的任务调度与数据均衡,彻底解决操作系统线程模型在 OLAP 场景下的固有缺陷。
长按下方二维码,回复「0113」加入 Doris 社区交流群,免费领取企业实践案例集,获取技术帮助,并与更多开发者和用户交流互动。

- END -
更多标杆企业信赖
智慧金融与政企:东北证券|国金证券|国信证券|杭银消金|杭州银行|河北幸福消费金融|河南农商银行|汇添富基金|金融壹账通|陆金所控股|霖梓控股|拉卡拉|平安人寿 | Planet|奇富科技|上海证券 | 同程数科|通联支付|泰康养老|无锡锡商银行|星云零售信贷|星火保 | 宇信科技|银联商务|易生支付|浙江头部银行|招商信诺人寿|招联金融|中信银行信用卡中心|中泰证券|360 数科|360 企业安全浏览器
互联网与文娱:菜鸟|抖音集团|斗鱼|叮咚买菜|浩瀚深度|京东|工商信息查询平台|货拉拉|快手|荔枝微课|票务平台|墨迹天气|MiniMax|奇安信|趣丸科技|顺丰科技|腾讯音乐|天眼查|网易|网易游戏|网易严选|网易云信|网易云音乐|小米|小鹅通|迅雷|约苗|字节跳动|知乎|360 商业化
企业服务与新经济:宝尊科技| 波司登|Cisco|橙联|度言|观测云|慧策|快成物流|领健|领创|灵犀科技|名创优品|Moka BI|美联物业|麦当劳|钱大妈|拈花云科|森马 |思必驰|顺丰科技|上海家化 | 物易云通|云积互动|有赞|雨润集团|纵腾集团|中通快递
先进智造与电信:爱玛|长安汽车|海信集团|极越汽车|金风科技|科大讯飞|岚图汽车|Lifewit|哪吒科技|四川航空|上海通用五菱|三星电子|蜀海供应链|特步|天翼云|雅迪|中国联通

作为基于 Apache Doris 的商业化公司,飞轮科技秉承着 “开源技术创新”和“实时数仓服务”双轮驱动的战略,在投入资源大力参与 Apache Doris 社区研发和推广的同时,基于 Apache Doris 内核打造了聚焦于企业大数据实时分析需求的企业级产品 SelectDB ,面向新一代需求打造世界领先的实时分析能力。自 2022 年成立以来,获得 IDG 资本、红杉中国、襄禾资本等顶级 VC 的近 10 亿元融资,创下了近年来开源基础软件领域的新纪录。




