You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

默认new运算符与std::malloc的性能差异探究

问题:重载new/delete为std::malloc/std::free的模板类性能优于原生类的原因

我用Google Benchmark测试时,实现了一个将new/delete重载为std::malloc/std::free的模板类Standard<T>,测试发现Standard<T>的性能略优于原生类T。按道理默认的new/delete应该是基于std::malloc/std::free实现的,为什么会存在性能差异?

核心重载代码:

template <typename Derived>
class Standard
{
  public:
    static void* operator new(std::size_t size) {
        return std::malloc(size);
    }

    static void operator delete(void *ptr, std::size_t) {
        std::free(ptr);
    }
};

完整可编译测试代码:

#include "evelyn/PoolPolicy.h"
#include <cstdint>
#include <benchmark/benchmark.h>

template <size_t N>
struct Block
{
    int8_t data[N];
};

template <typename T>
class Standard
{
public:
    static void *operator new(std::size_t size)
    {
        return std::malloc(size);
    }

    static void operator delete(void *ptr, std::size_t)
    {
        std::free(ptr);
    }
};


using A = Standard<int>;
using B = int;

class PoolFixture : public benchmark::Fixture
{
public:
    void SetUp(const ::benchmark::State &state)
    {
    }

    void TearDown(const ::benchmark::State &state)
    {
    }
};

auto constexpr ITERATE_TIME = 1000;

BENCHMARK_F(PoolFixture, without_pool_only_allocate)
(benchmark::State &state)
{
    using Block = Block<10>;
    for (auto _ : state)
    {
        for (auto i = 0; i < ITERATE_TIME; ++i)
        {
            auto ptr = new Block;
        }
    }
}

BENCHMARK_F(PoolFixture, with_pool_only_allocate)
(benchmark::State &state)
{
    using Block = Standard<Block<10>>;
    for (auto _ : state)
    {
        for (auto i = 0; i < ITERATE_TIME; ++i)
        {
            auto ptr = new Block;
        }
    }
}

BENCHMARK_F(PoolFixture, without_pool_allocate_and_delete_1)
(benchmark::State &state)
{
    using Block = Block<10>;
    for (auto _ : state)
    {
        for (auto i = 0; i < ITERATE_TIME; ++i)
        {
            auto ptr = new Block;
            delete ptr;
        }
    }
}

BENCHMARK_F(PoolFixture, with_pool_allocate_and_delete_1)
(benchmark::State &state)
{
    using Block = Standard<Block<10>>;
    for (auto _ : state)
    {
        for (auto i = 0; i < ITERATE_TIME; ++i)
        {
            auto ptr = new Block;
            delete ptr;
        }
    }
}
可能的原因分析
  • 默认new/delete的额外操作:标准仅要求默认new/delete最终调用malloc/free,但允许实现添加额外逻辑。比如:

    • 默认new在分配失败时会抛出std::bad_alloc异常,而你的重载直接返回nullptr,节省了异常处理的开销;
    • 部分编译器的默认new会加入轻量内存检查、对齐校验或分配统计逻辑(即使Release模式下也可能存在),你的重载完全跳过了这些步骤。
  • 编译器优化差异:

    • 自定义重载的逻辑更简单,编译器更容易做内联或激进优化;默认实现因包含更多分支(如异常处理、线程安全检查),优化难度更高;
    • 对于原生类型或简单结构体,编译器对默认new可能有特殊处理逻辑,而你的重载模板让编译器将其视为普通类分配,触发了更高效的优化路径。
  • 线程安全的细微差异:

    • 虽然malloc/free本身是线程安全的,但默认new/delete可能做了额外的线程安全封装(比如更粗粒度的锁),而你的重载直接调用malloc/free,刚好匹配测试场景的线程模型,减少了锁开销。
  • 编译模式与选项影响:

    • 如果测试未在纯净Release模式下运行,默认new/delete可能残留调试钩子(如_CRTDBG_MAP_ALLOC),而你的重载不受这些钩子影响;
    • 不同编译优化选项(如-O2/-O3)对默认实现和自定义重载的优化效果不同,比如默认实现的某些分支被编译器保留,而你的重载被完全优化为直接调用malloc/free。
  • 内存对齐与块大小差异:

    • 默认new可能为了满足严格对齐或内存池块大小要求,分配比实际需求更大的内存;而你的重载直接按传入size调用malloc,返回的内存刚好满足最小对齐要求,间接提升了缓存命中率,进而优化性能。

内容的提问来源于stack exchange,提问作者Junhui Zhu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:23:24