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

如何单元测试自定义类容器的memcpy拷贝优化是否生效?

验证自定义容器拷贝构造的memcpy优化方案

针对自定义容器的memcpy优化验证,核心是确认可平凡复制类型在拷贝时未调用元素的拷贝构造函数,而是直接通过内存块复制完成。以下是几种可行的测试方法:

方法1:对比平凡/非平凡类型的拷贝构造调用次数

通过构造两种测试类型,分别验证拷贝行为:

  • 非平凡复制类型:显式定义带计数的拷贝构造函数,验证容器拷贝时会逐个调用元素的拷贝构造:
#include <atomic>
#include <cassert>

struct NonTrivialFoo {
    int val;
    static std::atomic<int> copy_count;

    NonTrivialFoo(int v = 0) : val(v) {}
    NonTrivialFoo(const NonTrivialFoo& other) : val(other.val) {
        copy_count++;
    }
};

std::atomic<int> NonTrivialFoo::copy_count = 0;

// 测试非平凡类型的拷贝次数
void test_non_trivial_copy() {
    my_vector<NonTrivialFoo> v1{NonTrivialFoo{1}, NonTrivialFoo{2}, NonTrivialFoo{3}};
    NonTrivialFoo::copy_count = 0;
    my_vector<NonTrivialFoo> v2 = v1;
    assert(NonTrivialFoo::copy_count == 3); // 确认每个元素都触发了拷贝构造
}
  • 可平凡复制类型:使用默认拷贝构造的结构体,由于拷贝构造是平凡的,若容器使用memcpy优化,不会执行任何用户逻辑(计数不会变化)。同时验证拷贝后的内容与原容器完全一致:
struct TrivialFoo {
    int val;
    static std::atomic<int> copy_count;
    // 使用默认拷贝构造,确保std::is_trivially_copyable_v<TrivialFoo>为true
};

std::atomic<int> TrivialFoo::copy_count = 0;

void test_trivial_copy() {
    my_vector<TrivialFoo> v1{TrivialFoo{1}, TrivialFoo{2}, TrivialFoo{3}};
    TrivialFoo::copy_count = 0;
    my_vector<TrivialFoo> v2 = v1;

    // 验证内容一致
    for (size_t i = 0; i < v1.size(); ++i) {
        assert(v1[i].val == v2[i].val);
    }
    // 确认拷贝构造未被调用(平凡拷贝构造无用户逻辑,计数保持0)
    assert(TrivialFoo::copy_count == 0);
}

方法2:调试器断点跟踪

直接在调试器中给TrivialFoo的拷贝构造函数设置断点:

  • 若容器使用memcpy优化,断点不会触发;
  • 若容器逐个调用拷贝构造,断点会触发N次(N为容器元素个数)。

方法3:编译期静态断言验证逻辑分支

在容器的拷贝构造函数中,通过if constexpr区分平凡/非平凡类型的处理逻辑,同时添加嵌套类型特征供测试断言:

#include <type_traits>
#include <cstdlib>
#include <cstring>

template <typename T>
class my_vector {
public:
    // 暴露编译期特性:是否使用memcpy优化
    using uses_memcpy_for_copy = std::bool_constant<std::is_trivially_copyable_v<T>>;

    my_vector(const my_vector& other) {
        if constexpr (std::is_trivially_copyable_v<T>) {
            // memcpy优化路径
            data_ = allocate(other.size_);
            std::memcpy(data_, other.data_, other.size_ * sizeof(T));
            size_ = other.size_;
            capacity_ = other.capacity_;
        } else {
            // 逐个拷贝构造路径
            data_ = allocate(other.size_);
            size_ = other.size_;
            capacity_ = other.capacity_;
            for (size_t i = 0; i < size_; ++i) {
                new (data_ + i) T(other.data_[i]);
            }
        }
    }

    // 其他成员函数:size()、operator[]等
    size_t size() const { return size_; }
    T& operator[](size_t idx) { return data_[idx]; }
    const T& operator[](size_t idx) const { return data_[idx]; }

private:
    T* data_ = nullptr;
    size_t size_ = 0;
    size_t capacity_ = 0;

    T* allocate(size_t n) {
        return static_cast<T*>(std::malloc(n * sizeof(T)));
    }
};

然后在单元测试中添加静态断言,确保优化逻辑正确触发:

static_assert(std::is_trivially_copyable_v<TrivialFoo>);
static_assert(my_vector<TrivialFoo>::uses_memcpy_for_copy::value);

static_assert(!std::is_trivially_copyable_v<NonTrivialFoo>);
static_assert(!my_vector<NonTrivialFoo>::uses_memcpy_for_copy::value);

方法4:编译器插桩统计调用

使用GCC/Clang的-finstrument-functions编译选项,为函数调用插桩统计:

  1. 编写插桩函数,记录TrivialFoo拷贝构造函数的调用次数;
  2. 编译测试代码并运行,若调用次数为0,则说明容器使用了memcpy优化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:52:35