Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/148.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/jsf/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++ 优化c+中的指针副本+;_C++_Pointers_Optimization - Fatal编程技术网

C++ 优化c+中的指针副本+;

C++ 优化c+中的指针副本+;,c++,pointers,optimization,C++,Pointers,Optimization,所以今天我尝试优化链表遍历。我的想法是,将cur复制到最后一个,然后再复制到cur的旁边,效率较低,而我只能复制一个cur。希望下面的代码有助于使其更清晰: struct Node{ int body; Node* next; }; Node* construct(int len){ Node *head, *ptr, *end; head = new Node(); ptr = head; ptr->body = 0; for(i

所以今天我尝试优化链表遍历。我的想法是,将cur复制到最后一个,然后再复制到cur的旁边,效率较低,而我只能复制一个cur。希望下面的代码有助于使其更清晰:

struct Node{
    int body;
    Node* next;
};

Node* construct(int len){
    Node *head, *ptr, *end;
    head = new Node();
    ptr = head;
    ptr->body = 0;
    for(int i=1; i<len; i++){
        end = new Node();
        end->next = NULL;
        end->body = i;

        ptr->next = end;
        ptr = end;
    }
    return head;
}

int len(Node* ptr){
    int i=1;
    while(ptr->next){
        ptr = ptr->next;
        i += 1;
    }
    return i;
}

void trim(Node* head){
    Node *last, *cur;
    cur = head;
    while(cur->next){
        last = cur;
        cur = cur->next;
    }
    last->next = NULL;
}

void tumble_trim(Node* head){ // This one uses less copies per traverse
    Node *a, *b;
    a = head;
    while(true){
        if(!a->next){
            b->next = NULL;
            break;
        }
        b = a->next;
        if(!b->next){
            a->next = NULL;
            break;
        }
        a = b->next;
    }
}

int main(){
    int start;
    Node *head;

    start = clock();
    head = construct(100000);
    for(int i=0; i<5000; i++){
        trim(head);
    }
    cout << clock()-start << endl;

    start = clock();
    head = construct(100000);
    for(int i=0; i<5000; i++){
        tumble_trim(head);
    }
    cout << clock()-start << endl;
}

有人能解释为什么翻滚修剪()函数如此之慢吗?

您的编译器显然在优化
trim()
比它能优化的要多得多。这是一个保持代码简单易读的最好例子,只有在通过性能分析发现瓶颈后才尝试任何优化。即使这样,你也很难在这样一个简单的循环中击败编译器。

显然,你的编译器对
trim()
的优化程度远远超过了它的
rollup\u trim()
。这是一个保持代码简单易读的最好例子,只有在通过性能分析发现瓶颈后才尝试任何优化。即使这样,在这样一个简单的循环中,您也很难击败编译器。

以下是生成的两个函数的程序集的相关部分:(只是while循环:

修剪:

滚柱装饰:

LBB3_1:                                 ## =>This Inner Loop Header: Depth=1
    movq    %rdi, %rax
    movq    8(%rax), %rdx
    testq   %rdx, %rdx
    je  LBB3_2
## BB#3:                                ##   in Loop: Header=BB3_1 Depth=1
    movq    8(%rdx), %rdi
    testq   %rdi, %rdi
    movq    %rdx, %rcx
    jne LBB3_1
## BB#4:
    movq    $0, 8(%rax)
    popq    %rbp
    ret
LBB3_2:
现在,让我们试着描述一下每种情况下会发生什么:

在trim中,执行以下步骤:

  • 复制3个指针大小的值
  • 测试while循环的条件
  • 如果条件满足,则跳到循环的开头
  • 换句话说,每个迭代包含3个副本、1个测试和1个跳转指令

    现在,您的智能优化翻滚修剪:

    LBB3_1:                                 ## =>This Inner Loop Header: Depth=1
        movq    %rdi, %rax
        movq    8(%rax), %rdx
        testq   %rdx, %rdx
        je  LBB3_2
    ## BB#3:                                ##   in Loop: Header=BB3_1 Depth=1
        movq    8(%rdx), %rdi
        testq   %rdi, %rdi
        movq    %rdx, %rcx
        jne LBB3_1
    ## BB#4:
        movq    $0, 8(%rax)
        popq    %rbp
        ret
    LBB3_2:
    
  • 复制2个指针大小的值
  • 测试断裂情况
  • 如果条件满足,则跳到循环的末尾
  • 否则复制指针大小的值
  • 测试while循环的条件
  • 复制指针大小的值
  • 跳转到循环的开头
  • 换句话说,在最终迭代中,当您退出循环时,执行的指令总数为:

    • 修剪:3个指针副本,1个比较
    • 翻滚修剪:2个指针,1个比较,1个跳跃
    在所有其他迭代中,总计数如下所示:

    • 修剪:3个指针副本,1个比较,1个跳转
    • 翻滚修剪:4次指针复制,2次比较,1次跳转
    因此,在极少数情况下(退出循环之前的最后一次迭代),当且仅当跳转指令比将指针大小的值从寄存器复制到寄存器(事实并非如此)更便宜时,您的实现才更便宜

    在常见情况下(所有其他迭代,您的实现都有更多的副本和更多的比较。(和更多的指令,将更多的负载放在指令缓存上。和更多的分支语句,将更多的负载放在分支缓存上)

    现在,如果你一开始就担心性能,那么还有两件更基本的事情你做错了:

  • 您正在使用一个链表。链表之所以慢,是因为它们执行的算法(这涉及到在内存中跳跃,因为节点不是连续分配的),而不是因为实现。因此,无论您的实现多么聪明,它都无法弥补底层算法的糟糕
  • 您正在编写自己的链接列表。如果您必须使用链接列表,请使用专家编写的链接列表(
    std::list

  • 以下是为两个函数生成的程序集的相关部分:(仅while循环):

    修剪:

    滚柱装饰:

    LBB3_1:                                 ## =>This Inner Loop Header: Depth=1
        movq    %rdi, %rax
        movq    8(%rax), %rdx
        testq   %rdx, %rdx
        je  LBB3_2
    ## BB#3:                                ##   in Loop: Header=BB3_1 Depth=1
        movq    8(%rdx), %rdi
        testq   %rdi, %rdi
        movq    %rdx, %rcx
        jne LBB3_1
    ## BB#4:
        movq    $0, 8(%rax)
        popq    %rbp
        ret
    LBB3_2:
    
    现在,让我们试着描述一下每种情况下会发生什么:

    在trim中,执行以下步骤:

  • 复制3个指针大小的值
  • 测试while循环的条件
  • 如果条件满足,则跳到循环的开头
  • 换句话说,每个迭代包含3个副本、1个测试和1个跳转指令

    现在,您的智能优化翻滚修剪:

    LBB3_1:                                 ## =>This Inner Loop Header: Depth=1
        movq    %rdi, %rax
        movq    8(%rax), %rdx
        testq   %rdx, %rdx
        je  LBB3_2
    ## BB#3:                                ##   in Loop: Header=BB3_1 Depth=1
        movq    8(%rdx), %rdi
        testq   %rdi, %rdi
        movq    %rdx, %rcx
        jne LBB3_1
    ## BB#4:
        movq    $0, 8(%rax)
        popq    %rbp
        ret
    LBB3_2:
    
  • 复制2个指针大小的值
  • 测试断裂情况
  • 如果条件满足,则跳到循环的末尾
  • 否则复制指针大小的值
  • 测试while循环的条件
  • 复制指针大小的值
  • 跳转到循环的开头
  • 换句话说,在最终迭代中,当您退出循环时,执行的指令总数为:

    • 修剪:3个指针副本,1个比较
    • 翻滚修剪:2个指针,1个比较,1个跳跃
    在所有其他迭代中,总计数如下所示:

    • 修剪:3个指针副本,1个比较,1个跳转
    • 翻滚修剪:4次指针复制,2次比较,1次跳转
    因此,在极少数情况下(退出循环之前的最后一次迭代),当且仅当跳转指令比将指针大小的值从寄存器复制到寄存器(事实并非如此)更便宜时,您的实现才更便宜

    在常见情况下(所有其他迭代,您的实现都有更多的副本和更多的比较。(和更多的指令,将更多的负载放在指令缓存上。和更多的分支语句,将更多的负载放在分支缓存上)

    现在,如果你一开始就担心性能,那么还有两件更基本的事情你做错了:

  • 您正在使用一个链表。链表之所以慢,是因为它们执行的算法(这涉及到在内存中跳跃,因为节点不是连续分配的),而不是因为实现。因此,无论您的实现多么聪明,它都无法弥补底层算法的糟糕
  • 您正在编写自己的链接列表。如果您必须使用链接列表,请使用专家编写的链接列表(
    std::list

  • 您在发行版中编译过这个基准测试吗?另外,请注意,一般来说,
    clock()
    对于度量对性能敏感的代码来说是一个糟糕的函数(它只精确到