我是一名有用的助手,可以为您进行翻译。
我有一个二维数组,每一行都包含6个已按升序排列的整数。例如:
1 2 3 4 5 6
6 8 9 10 13 15
1 4 5 6 7 9
1 4 5 6 7 8
3 18 19 20 25 34
期望输出:
1 2 3 4 5 6
1 4 5 6 7 8
1 4 5 6 7 9
3 18 19 20 25 34
6 8 9 10 13 15
实际数据包含8m到33m个这样的记录。我正在尝试确定最快的方法来对此数组进行排序。我目前有一些使用qsort的工作代码:
qsort调用:
qsort(allRecords, lineCount, sizeof(int*), cmpfunc);
cmpfunc:
int cmpfunc (const void * a, const void * b)
{
const int *rowA = *(const int **)a;
const int *rowB = *(const int **)b;
if (rowA[0] > rowB[0]) return 1;
if (rowA[0] < rowB[0]) return -1;
if (rowA[1] > rowB[1]) return 1;
if (rowA[1] < rowB[1]) return -1;
if (rowA[2] > rowB[2]) return 1;
if (rowA[2] < rowB[2]) return -1;
if (rowA[3] > rowB[3]) return 1;
if (rowA[3] < rowB[3]) return -1;
if (rowA[4] > rowB[4]) return 1;
if (rowA[4] < rowB[4]) return -1;
if (rowA[5] > rowB[5]) return 1;
if (rowA[5] < rowB[5]) return -1;
return 0;
}
对于这个样本3300万条记录,它大约需要35.6秒(gcc -O1),速度相当快,但我想知道在每行中给定预排序值的情况下是否有更快的方法来执行此操作。
这自然导致了最常见的第一个数字是1的数据,因此在33m的记录文件中,可能有1200万条以1开头的记录,然后是800万条以2开头的记录,500万条以3开头的记录等等......我不确定这是否适合一种特定类型的排序(例如堆排序)。
我的理解是qsort由于所有调用函数的次数而具有相当多的开销,因此我希望能够获得更快的性能。
我通常不编写C代码,因此我非常乐意接受建议和批评,因为我正在从教程和其他StackOverflow问题/答案中拼凑这些内容。
编辑:按要求,我的初始化代码:
// Empty record
int recArray[6] = {0,0,0,0,0,0};
// Initialize allRecords
int** allRecords;
allRecords = (int**) malloc(lineCount*sizeof(int*));
for(i=0; i < lineCount; i++)
{
allRecords[i] = (int*) malloc(6*sizeof(int));
}
// Zero-out all records
for(i=0; i < lineCount; i++)
{
memcpy(allRecords[i], recArray, 6 * sizeof(int));
}
我还在学习正确的做法,所以如果我做错了一切,我也不会感到惊讶。希望能得到正确方面的指导。
其他人问到值的范围 - 我不确定未来这个范围是否会更改,但目前值的范围在1到99之间。
此外,对于性能分析 - 我编写了一个小函数,使用 gettimeofday() 获取秒/微秒,然后进行比较。我很愿意接受更好的方法。输出的结果如下:
// <-- Here I capture the gettimeofday() structure output
Sorting...
Sorted.
Time Taken: 35.628882s // <-- Capture it again, show the difference
更新: 根据@doynax的建议,我现在将每行的6个值“打包”为一个无符号长整型:
// Initialize allRecords
unsigned long long int* allRecords;
allRecords = (unsigned long long int*) malloc(lineCount*sizeof(unsigned long long int));
for(i=0; i < lineCount; i++)
{
allRecords[i] = 0;
}
...
// "Pack" current value (n0) into an unsigned long long int
if(recPos == 0) { lineSum += n0 * UINT64_C(1); }
else if(recPos == 1) { lineSum += n0 * UINT64_C(100); }
else if(recPos == 2) { lineSum += n0 * UINT64_C(10000); }
else if(recPos == 3) { lineSum += n0 * UINT64_C(1000000); }
else if(recPos == 4) { lineSum += n0 * UINT64_C(100000000); }
else if(recPos == 5) { lineSum += n0 * UINT64_C(10000000000); }
...
allRecords[linecount] = lineSum;
lineSum = 0;
我可以将其中一个unsigned long long int值“解包”成原始的6个int值。但是,当我尝试排序时:
qsort(allRecords, lineCount, sizeof(unsigned long long int), cmpfunc);
...
int cmpfunc (const void * a, const void * b)
{
if (*(unsigned long long int*)a > *(unsigned long long int*)b) return 1;
if (*(unsigned long long int*)a < *(unsigned long long int*)b) return -1;
return 0;
}
结果没有按照预期排序。如果我使用以下代码显示排序前后的第一行和最后一行:
printf("[%i] = %llu = %i,%i,%i,%i,%i,%i\n", j, lineSum, recArray[0]...recArray[5]);
输出结果为:
First and last 5 rows before sorting:
[#] = PACKED INT64 = UNPACKED
[0] = 462220191706 = 6,17,19,20,22,46
[1] = 494140341005 = 5,10,34,40,41,49
[2] = 575337201905 = 5,19,20,37,53,57
[3] = 504236262316 = 16,23,26,36,42,50
[4] = 534730201912 = 12,19,20,30,47,53
[46] = 595648302516 = 16,25,30,48,56,59
[47] = 453635251108 = 8,11,25,35,36,45
[48] = 403221161202 = 2,12,16,21,32,40
[49] = 443736310604 = 4,6,31,36,37,44
[50] = 575248312821 = 21,28,31,48,52,57
First and last 5 rows after sorting:
[0] = 403221161202 = 2,12,16,21,32,40
[1] = 413218141002 = 2,10,14,18,32,41
[2] = 443736310604 = 4,6,31,36,37,44
[3] = 444127211604 = 4,16,21,27,41,44
[4] = 453028070302 = 2,3,7,28,30,45
[46] = 585043260907 = 7,9,26,43,50,58
[47] = 593524170902 = 2,9,17,24,35,59
[48] = 595248392711 = 11,27,39,48,52,59
[49] = 595251272612 = 12,26,27,51,52,59
[50] = 595648302516 = 16,25,30,48,56,59
我猜测我正在比较错误的值(例如指针值而不是实际值),但我不太确定正确的语法是什么。
另一方面,这种方式非常快。
排序3300万64位整数需要大约4-5秒钟(至少在其当前错误的形式中)。
malloc
的结果。在基准测试/发布时至少使用-O2
或-O3
,不要使用-O1
。 - phuclv