Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ iota、generate和手动循环是否都执行相同的操作?_C++_Performance_Vector - Fatal编程技术网

C++ iota、generate和手动循环是否都执行相同的操作?

C++ iota、generate和手动循环是否都执行相同的操作?,c++,performance,vector,C++,Performance,Vector,这三种填充向量的方法之间是否存在性能差异 #include <vector> #include <numeric> #include <algorithm> #include <iterator> int main() { std::vector<int> v(10); std::iota(v.begin(), v.end(), 0); std::vector<int> v2(10); i

这三种填充向量的方法之间是否存在性能差异

#include <vector>
#include <numeric>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<int> v(10);
    std::iota(v.begin(), v.end(), 0);

    std::vector<int> v2(10);
    int i = 0;
    std::generate(v2.begin(), v2.end(), [&i](){return i++; });

    std::vector<int> v3(10);
    i = 0;
    for (auto& j : v3)
    {
        j = i++;
    }

return 0;
}

我知道它们都产生相同的结果,我感兴趣的只是知道对于更大的向量是否有速度差。对于不同的类型,答案会有所不同吗?

您忘了提到另外一个标准算法-算法std::for_

比如说

std::vector<int> v4(10);
int i = 0;
std::for_each(v4.begin(), v4.end(), [&i]( int &item ){ item = i++; } );
算法和基于范围的for语句之间没有任何本质区别。事实上,它们相互复制。例如,基于范围的For语句使用相同的方法begin和end

所以最好注意表现力。在这种情况下,我更喜欢std::iota


另外,也许阅读genetal文本会很有趣,尽管它是用俄语编写的,但您可以使用谷歌服务翻译(google service translate)等工具阅读。

您忘了提及另一个标准算法-算法std::for_

比如说

std::vector<int> v4(10);
int i = 0;
std::for_each(v4.begin(), v4.end(), [&i]( int &item ){ item = i++; } );
算法和基于范围的for语句之间没有任何本质区别。事实上,它们相互复制。例如,基于范围的For语句使用相同的方法begin和end

所以最好注意表现力。在这种情况下,我更喜欢std::iota


另外,虽然genetal文本是用俄语编写的,但您可以使用google service translate阅读。我们可以查看我使用的输出程序集gcc-03,以及您的代码:

1第一版,带std::iota:

带有std::generate和Lambda的2个版本:

main:
    sub rsp, 8
    mov edi, 40
    call    operator new(unsigned long)
    mov DWORD PTR [rax], 0
    mov DWORD PTR [rax+4], 1
    mov rdi, rax
    mov DWORD PTR [rax+8], 2
    mov DWORD PTR [rax+12], 3
    mov DWORD PTR [rax+16], 4
    mov DWORD PTR [rax+20], 5
    mov DWORD PTR [rax+24], 6
    mov DWORD PTR [rax+28], 7
    mov DWORD PTR [rax+32], 8
    mov DWORD PTR [rax+36], 9
    call    operator delete(void*)
    xor eax, eax
    add rsp, 8
    ret
3和最新版本,带有手写循环:

main:
    sub rsp, 8
    mov edi, 40
    call    operator new(unsigned long)
    mov DWORD PTR [rax], 0
    mov DWORD PTR [rax+4], 1
    mov rdi, rax
    mov DWORD PTR [rax+8], 2
    mov DWORD PTR [rax+12], 3
    mov DWORD PTR [rax+16], 4
    mov DWORD PTR [rax+20], 5
    mov DWORD PTR [rax+24], 6
    mov DWORD PTR [rax+28], 7
    mov DWORD PTR [rax+32], 8
    mov DWORD PTR [rax+36], 9
    call    operator delete(void*)
    xor eax, eax
    add rsp, 8
    ret
结论:

正如预期的那样,这三种方法都会生成相同的程序集,所有这些程序集都使用一个合适的编译器展开,并启用了优化

因此,不存在性能差异

注:

我做了一个测试,比较了向量大到不能展开循环的程序集。我不知道GCC启发式,但它是从大于15的大小开始的


在这种情况下,对于所有3种情况,程序集仍然是相同的,我不会在这里复制输出,因为它不会带来太多的答案,但问题是编译器确实非常擅长优化这类代码。

我们可以看看我使用的输出程序集gcc-03,以及您的代码:

1第一版,带std::iota:

带有std::generate和Lambda的2个版本:

main:
    sub rsp, 8
    mov edi, 40
    call    operator new(unsigned long)
    mov DWORD PTR [rax], 0
    mov DWORD PTR [rax+4], 1
    mov rdi, rax
    mov DWORD PTR [rax+8], 2
    mov DWORD PTR [rax+12], 3
    mov DWORD PTR [rax+16], 4
    mov DWORD PTR [rax+20], 5
    mov DWORD PTR [rax+24], 6
    mov DWORD PTR [rax+28], 7
    mov DWORD PTR [rax+32], 8
    mov DWORD PTR [rax+36], 9
    call    operator delete(void*)
    xor eax, eax
    add rsp, 8
    ret
3和最新版本,带有手写循环:

main:
    sub rsp, 8
    mov edi, 40
    call    operator new(unsigned long)
    mov DWORD PTR [rax], 0
    mov DWORD PTR [rax+4], 1
    mov rdi, rax
    mov DWORD PTR [rax+8], 2
    mov DWORD PTR [rax+12], 3
    mov DWORD PTR [rax+16], 4
    mov DWORD PTR [rax+20], 5
    mov DWORD PTR [rax+24], 6
    mov DWORD PTR [rax+28], 7
    mov DWORD PTR [rax+32], 8
    mov DWORD PTR [rax+36], 9
    call    operator delete(void*)
    xor eax, eax
    add rsp, 8
    ret
结论:

正如预期的那样,这三种方法都会生成相同的程序集,所有这些程序集都使用一个合适的编译器展开,并启用了优化

因此,不存在性能差异

注:

我做了一个测试,比较了向量大到不能展开循环的程序集。我不知道GCC启发式,但它是从大于15的大小开始的


在这种情况下,对于所有3种情况,程序集仍然是相同的,我不会在这里复制输出,因为它不会带来太多的答案,但问题是编译器确实非常擅长优化这类代码。

正确的方法当然是测量和/或比较生成的代码。由于std::vector对T类型的对象使用连续内存,编译器很可能看穿所有3个版本的循环并生成几乎相同的代码。此外,对于设置中的特定算法,智能实现几乎没有什么作用。情况可能会有所不同,例如,当使用std::deque时,算法可以单独处理段以提高性能,但我不知道有任何实现实际上会这样做


如果性能是您最关心的问题,并且您正在使用大向量,那么您可能不希望最初创建大向量,因为这可能会触及所有内存,尽管它即将被覆盖。相反,您应该构造并清空向量,保留足够的内存,然后使用合适的目标迭代器,例如std::back_inserterv。不过,这些方法需要适当改变。在算法中构造对象时,算法实际上可以应用一些简单循环使用的智能,例如push_back或合适的附加迭代器可能不适用:因为算法可以看到他们将要创建多少对象,尽管需要通过迭代器类型进行一些特殊访问,但它们可以根据循环外的容量进行检查。即使在算法中没有优化,我也希望在向量上做一次单次传递比算法中的任何调整对性能有更大的好处。

正确的方法当然是测量和/或比较生成的代码。由于std::vector对T类型的对象使用连续内存,编译器很可能看穿所有3个版本的循环并生成几乎相同的代码。此外,还有一个非常小的智能impl 校正可以用于设置中的特定算法。情况可能会有所不同,例如,当使用std::deque时,算法可以单独处理段以提高性能,但我不知道有任何实现实际上会这样做


如果性能是您最关心的问题,并且您正在使用大向量,那么您可能不希望最初创建大向量,因为这可能会触及所有内存,尽管它即将被覆盖。相反,您应该构造并清空向量,保留足够的内存,然后使用合适的目标迭代器,例如std::back_inserterv。不过,这些方法需要适当改变。在算法中构造对象时,算法实际上可以应用一些简单循环使用的智能,例如push_back或合适的附加迭代器可能不适用:因为算法可以看到他们将要创建多少对象,尽管需要通过迭代器类型进行一些特殊访问,但它们可以根据循环外的容量进行检查。即使在算法中没有优化,我也希望在向量上做一次单次传递比算法中的任何调整对性能有更大的好处。

@WhozCraig他们不是都对初始化10个元素进行了赋值吗?@juanchopanza事实上,是的,他们对性能有好处,现在我盯着他们看。我会保留并重新插入generate\n,这让我陷入了后者的低效状态。你完全正确。真的需要在星期天停止阅读。谢谢你让我保持诚实。放弃先前的评论。无论如何,我在标准中找不到任何东西可以给这三个变体中的任何一个带来性能优势。如果有,它将取决于实现。当然,对于用户定义的类型,使用pre-increment可能会给iota带来优势。@WhozCraig:我认为reserve和std::back_inserter可以做得很好,但实际上,似乎只有在Mac上使用最新版本的gcc和clang with-O3尝试真正大的向量才有回报。最有可能的是,对容量的检查和对向量大小的摆弄破坏了保留方法的性能:他们需要一个定制的算法版本,以智能的方式避免这些成本。@DietmarKühl我也这么认为。我主要将其用于具有非默认构造的对象的向量,或者用于非常大的向量,而不考虑类型。通常我对这样一个工件的性能不太感兴趣,除非它是一个非常关键的算法,也许这就是。@WhozCraig他们不是都对初始化10个元素感兴趣吗?@juanchopanza事实上,是的,他们对,现在我盯着他们看。我会保留并重新插入generate\n,这让我陷入了后者的低效状态。你完全正确。真的需要在星期天停止阅读。谢谢你让我保持诚实。放弃先前的评论。无论如何,我在标准中找不到任何东西可以给这三个变体中的任何一个带来性能优势。如果有,它将取决于实现。当然,对于用户定义的类型,使用pre-increment可能会给iota带来优势。@WhozCraig:我认为reserve和std::back_inserter可以做得很好,但实际上,似乎只有在Mac上使用最新版本的gcc和clang with-O3尝试真正大的向量才有回报。最有可能的是,对容量的检查和对向量大小的摆弄破坏了保留方法的性能:他们需要一个定制的算法版本,以智能的方式避免这些成本。@DietmarKühl我也这么认为。我主要将其用于具有非默认构造的对象的向量,或者用于非常大的向量,而不考虑类型。通常,我对这样一个工件的性能不太感兴趣,除非它是一个非常关键的算法,也许这是值得注意的+1。一个64位汇编程序和一个优秀的优化器将把它压缩到一个非常小的MOV数量,实际上是一半+值得注意的是,一个64位汇编程序和一个像样的优化器将把这个问题压缩到一个非常小的MOV量——实际上是一半。