【问题标题】:faster way than memcpy to copy 0-terminated string比 memcpy 更快的方法来复制 0 终止的字符串
【发布时间】:2017-12-21 00:14:15
【问题描述】:

我有一个关于复制以 0 结尾的字符串的问题:

const char * str = "Hello World !";
size_t getSize = strlen(str);
char * temp = new char[getSize + 1];

...我知道我可以使用这个功能

memcpy(temp, str, getSize);

但我想使用我自己的复制功能,它有这样的作用

int Count = 0;
while (str[Count] != '\0') {
    temp[Count] = str[Count];
    Count++;
}

这两种方式都是正确的并且成功了。现在我想检查 1000 万次并为 memcpy 执行此操作

const char * str = "Hello World !";
size_t getSize = strlen(str);
for (size_t i = 0; i < 10000000; i++) {
    char * temp = new char[getSize + 1];
    memcpy(temp, str, getSize);
}

这是我自己的方式

    const char * str = "Hello World !";
    size_t getSize = strlen(str);
    for (size_t i = 0; i < 10000000; i++) {
        char * temp = new char[getSize + 1];
        int Count = 0;
        while (str[Count] != '\0') {
            temp[Count] = str[Count];
            Count++;
        }
    }

第一个过程在 420 毫秒 内完成,第二个过程在 650 毫秒 内完成 ... 为什么?这两种方式都是一样的!我想使用我自己的函数而不是 memcpy。有什么方法可以让我自己的方式更快(因为 memcpy 很快或者可能更快)? 我如何更新自己的方式(同时)以使其更快或与 memcpy 相等?

完整来源

int main() {

    const char * str = "Hello world !";
    size_t getSize = strlen(str);

    auto start_t = chrono::high_resolution_clock::now();
    for (size_t i = 0; i < 10000000; i++) {
        char * temp = new char[getSize + 1];
        memcpy(temp, str, getSize);
    }
    cout << chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - start_t).count() << " milliseconds\n";


    start_t = chrono::high_resolution_clock::now();
    for (size_t i = 0; i < 10000000; i++) {
        char * temp = new char[getSize + 1];
        int done = 0;
        while (str[done] != '\0') {
            temp[done] = str[done];
            done++;
        }
    }
    cout << chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now() - start_t).count() << " milliseconds\n";

    return 0;
}

结果:

482 毫秒
654 毫秒

【问题讨论】:

  • 如何衡量执行时间?
  • 依赖于数组末尾的'\0' 字符,与memcpy() 的作用不同。如果您只想处理这种情况,则使用strcpy() 可能比滚动您自己的函数更好(实现中可能使用某些技巧使其比您的实现更快)。
  • 为什么你认为你可以超越你的编译器标准库的创建者?
  • 我很确定 new 的成本比复制 14 个字节的成本相形见绌!
  • @myOwnWays 许多编译器实现使用汇编语言来复制缓冲区。再说一次,你为什么认为你可以比业内一些最优秀的程序员更聪明?

标签: c++ string memcpy strcpy strdup


【解决方案1】:

看到你没有使用指针并将你正在做的事情 (strcpy) 与 memcpy 进行比较清楚地表明你是一个初学者,正如其他人已经说过的那样,很难胜过像那些为你的库编写代码的经验丰富的程序员.

但我会给你一些提示来优化你的代码。 我快速浏览了 Microsoft 的 C 标准库实现(称为 C 运行时库),他们在汇编中执行它比在 C 中执行它更快。所以这是速度的一点。

在大多数具有 32 位总线的 32 位架构中,CPU 可以在一次请求内存中从内存中获取 32 位信息(假设数据正确对齐),但即使您需要 16 位或 8 位,它仍然需要提出 1 个请求。因此,使用机器的字长可能会加快速度。

最后,我想将您的注意力引向 SIMD。如果您的 CPU 提供它,您可以使用它并获得额外的速度。同样,MSCRT 有一些 SSE2 优化选项。

过去,我有时不得不编写优于我的库实现的代码,因为我有特定的需求或可以优化的特定类型的数据,而且除非特别需要,否则它可能具有一定的教育价值,您的时间最好花在实际代码上,而不是花在重新实现库函数上。

【讨论】:

    【解决方案2】:

    ...这两种方式都是一样的!

    不,他们不是:

    1. memcpy() 不会检查每个字符是否包含 '\0'
    2. 实施者所做的优化可能比您在 naive 方法中所做的更多

    您的方法不可能比memcpy() 更快。

    【讨论】:

    • 请删除您的答案,并将其作为评论发布。
    • @Khaled.K 为什么会这样?我的回答很好地解释了差异。
    • 那么 memcpy 如何创建从 str 到 temp 的副本?
    • 这不是答案,因为你回答的不是问题。
    • @Khaled.K 它确实回答了“实现是相同的,这怎么会发生?”问题
    【解决方案3】:

    用您自己的函数替换库函数通常会导致性能下降。

    memcpy 代表一个非常基本的内存操作。因此,它的作者对其进行了高度优化。与“幼稚”的实现不同,库版本尽可能一次移动多个字节,并在可用的平台上使用硬件辅助。

    此外,编译器本身“知道”memcpy 和其他库函数的内部工作原理,并且可以针对编译时已知长度的情况完全优化它们。

    注意:您的实现具有strcpy 的语义,而不是memcpy

    【讨论】:

    • 好的,我想知道怎么做!!! 100% memcpy 检查每个字符! (一个一个地复制......)所以为什么它必须更快!!!
    • @myOwnWays 阅读库实现源代码和/或检查发布(优化)构建中生成的汇编器。
    • @myOwnWays "memcpy 也检查每个字符!" 嗯?不,它没有。
    • @myOwnWays 不,它没有 - memcpy 按长度而不是空终止符(它是 strcpy,按空终止符)。
    猜你喜欢
    • 2013-06-05
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 2014-02-01
    • 2022-03-30
    相关资源
    最近更新 更多