C-排序具有可变长度元素的大二进制文件

时间:2018-11-16 22:59:43

标签: c performance sorting io binaryfiles

我的问题如下:

  • 我在二进制文件中存储了一组长度可变的元素,我们将其称为data.bin
  • 每个元素都有一个排序关键字参数,我们称其为key,大小为size,在data.bin pos中的文件位置。这三个参数表示为结构。

问题在于如何有效地对data.bin进行排序。

现在我已经定义了一个数据结构

typedef struct {
    int key;
    unsigned int size;
    long int pos;
} index

和一个数组index indices[],其中包含存储在data.bin中的元素的值。使用快速排序算法根据key对该列表进行连续排序,该算法即使对于非常大量的条目(例如10M)也足够快。然后,我使用排序列表indices将排序后的data.bin文件写为sorted.bin。我的代码的核心如下(在这里,我故意删除了错误检查部分):

size_t mergeBuffIdx = 0;
char  *mergeBuff = (char *) calloc(MERGE_BUFF_SIZE, sizeof(char));

for (unsigned int idx = 0; idx < numEntries; idx++) {
    unsigned int dataSize = indices[idx].size;
    if ((mergeBuffIdx + dataSize) >= MERGE_BUFF_SIZE) {
            fwrite(mergeBuff, sizeof(unsigned char), mergeBuffIdx, sortedDataFile);
            mergeBuffIdx = 0;
    }

    // set the file pointer at the beginning of the data file position
    fseek(dataFile, indices[idx].pos, SEEK_SET);

    // read the data from the file as an unsigned char array
    fread(&mergeBuff[mergeBuffIdx], sizeof(unsigned char), dataSize, dataFile);
    mergeBuffIdx += dataSize;
}

// write remaining data
if (mergeBuffIdx != 0) {
    fwrite(mergeBuff, sizeof(unsigned char), mergeBuffIdx, sortedDataFile);
}

这种方法非常简单,当data.bin很大(我的一般用例是30GB),条目数约为10M且两个连续排序的条目可能很远时,很快就会变得很慢原始data.bin文件。对于1GB的data.bin文件,这种方法大约需要30分钟以上。即使我使用的是SSD HD。

您对这个问题有一些想法,替代解决方案或方法吗?

我尝试使用内存映射文件,它们具有相似甚至最差的性能。我认为瓶颈是fseek的调用,但是,我无法找出替代方案和更有效的方法。

感谢您的阅读, S

1 个答案:

答案 0 :(得分:0)

采用这种方法,每次读取仅获得一个元素,因此有1000万条命令根据排序的键读取文件。至少SSD可以消除随机访问开销。

使用外部合并排序直接对数据进行排序要比对键进行排序和进行随机访问传递要更快。这将允许一次使用读/写大约256 MB。最初的过程将读取256MB的块,对元素进行排序,然后写入256MB的块(其中128个为32GB)。 16路合并(2048个读取每个16 MB),然后进行8路合并(1024个读取,每个读取32 MB)将大约处理32GB的数据。对于16或8方式的合并,您可能要使用某种形式的优先级队列,例如堆。

您没有提及密钥/大小信息是否在单独的文件中。如果有足够的备用内存,则可以在外部合并排序期间将此信息保留在内存中,并在完成后将其写回。