SWE / Applied Scientist · Coding

Amazon 手写Memcpy:非对齐字节访问与重叠合同

先实现可移植字节复制,再讨论对齐、宽加载、尾部与硬件优化。

题目重点:先实现可移植字节复制,再讨论对齐、宽加载、尾部与硬件优化。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 输入给有效、可访问且互不重叠的N字节源和目标区间,返回目标起始指针。底层地址可能未按word对齐;NULL/无效地址不在合同内。
  • memcpy与memmove合同不同,重叠不支持。教学byte loop没有声称达到目标架构优化吞吐;具体CPU、cache、指令及页面策略未披露。

解题路线

  1. unsigned char指针逐字节读写对象表示,不把未对齐地址强转成word指针再解引用。循环第i步之前目标前i字节与源前i字节相同,其他目标字节未修改。
  2. 只访问范围[0,N),包括长度不足word、非整倍尾段和N0。宽加载优化即使最后只写少量,也不能越界读取下一个页面;不能用“多读没关系”忽略合法地址范围。
  3. 若要求对齐优化,先问硬件是否支持未对齐访问、源和目标对齐余数是否匹配,以及语言别名规则。相同余数可先复制头部到对齐位置,不同余数可能不能同时对齐。
  4. 真实库可能采用向量指令、展开、阈值和非临时写策略;面试先给正确基线,再设计受硬件/语言约束的优化并测量,不能把固定块大小当普遍最优。

手动走一遍

源地址offset3,目标offset5,N11:复制11字节并保持目标前5及后续哨兵不变;N0不访问字节仍返回destination。测试用独立源/目标buffer,未拿重叠作为memcpy正确性要求。

C 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

#include <stddef.h>

/* Caller supplies valid disjoint storage ranges. Not memmove.
 * Byte access handles arbitrary alignment; no typed word dereference. */
void *copy_bytes(void *restrict destination, const void *restrict source, size_t n) {
    unsigned char *out = (unsigned char *)destination;
    const unsigned char *in = (const unsigned char *)source;
    for (size_t i = 0; i < n; ++i) out[i] = in[i];
    return destination;
}

复杂度

字节基线O(N)时间、O(1)辅助空间。复制带宽受内存层级和N大小影响,复杂度相同不等于吞吐相同;峰值带宽与小对象调用开销需分开测。

易错点与追问

容易失分的边界

  • 把memcpy当成支持重叠的memmove。
  • 未对齐word解引用或跨合法末尾宽读。
  • 只测结果值,漏目标范围之外的破坏。

继续深挖

  • 如果要求memmove,为什么要按相对位置选择方向或临时buffer?
  • 怎样设计全部对齐offset和末尾长度组合的测试?

技术参考

✓已复制微信号!