【发布时间】:2018-03-30 11:10:14
【问题描述】:
在快速排序实现中,左侧的数据是纯-O2 优化代码,右侧数据是-O2 优化代码,-fno-optimize-sibling-calls 标志打开,即尾调用优化关闭。这是 3 次不同运行的平均值,变化似乎可以忽略不计。值的范围是 1-1000,时间以毫秒为单位。编译器为 MinGW g++,版本 6.3.0。
size of array with TLO(ms) without TLO(ms)
8M 35,083 34,051
4M 8,952 8,627
1M 613 609
下面是我的代码:
#include <bits/stdc++.h>
using namespace std;
int N = 4000000;
void qsort(int* arr,int start=0,int finish=N-1){
if(start>=finish) return ;
int i=start+1,j = finish,temp;
auto pivot = arr[start];
while(i!=j){
while (arr[j]>=pivot && j>i) --j;
while (arr[i]<pivot && i<j) ++i;
if(i==j) break;
temp=arr[i];arr[i]=arr[j];arr[j]=temp; //swap big guy to right side
}
if(arr[i]>=arr[start]) --i;
temp = arr[start];arr[start]=arr[i];arr[i]=temp; //swap pivot
qsort(arr,start,i-1);
qsort(arr,i+1,finish);
}
int main(){
srand(time(NULL));
int* arr = new int[N];
for(int i=0;i<N;i++) {arr[i] = rand()%1000+1;}
auto start = clock();
qsort(arr);
cout<<(clock()-start)<<endl;
return 0;
}
我听说clock() 不是衡量时间的完美方法。但这种效果似乎是一致的。
编辑:作为对评论的回应,我想我的问题是:解释 gcc 的尾调用优化器究竟是如何工作的,这是如何发生的,我应该如何利用尾调用来加速我的程序?
【问题讨论】:
-
你的问题是什么? “解释 gcc 的优化器是如何工作的以及这是如何发生的”或“我如何避免这个问题”或“我怎样才能让代码运行得更快”?
-
第一行数据应该是 35083,34051 吗?如果你把桌子变成一个固定宽度的桌子,中间有空格会很有帮助。
-
警告:这是一个糟糕的实现,因为递归调用可以很好地爆炸堆栈。总是先处理最短的子数组!
-
@YvesDaoust :当然,这只有在编译器真正消除尾调用时才有用!
-
ShihabShahriar:正如@YvesDaoust 所说,快速排序中尾调用的主要目的不是加快快速排序,而是防止堆栈爆炸。通过在较短的分区上递归,然后在较长的分区上循环,可以保证递归深度小于元素数量的log2,可以认为是大多数实用硬件上的小常数。 (例如,远小于 64)
标签: c++ algorithm optimization g++ compiler-optimization