【问题标题】:Most efficient way to reallocate while ordering values in memory?在内存中排序值时重新分配的最有效方法?
【发布时间】:2014-09-11 03:07:19
【问题描述】:

我分配了 32 字节的连续内存(使用 malloc)来保存八个连续的 32 位值。在这块内存上执行了某些任务后,值的布局如下:

+---+---+---+---+---+---+---+---+
| d |   |   |   |   | a | b | c |
+---+---+---+---+---+---+---+---+

注意到有一个洞吗?这些值不再需要并且可以忽略。该孔将始终是主分配大小的一半,并且始终是连续的。

在重新分配这块内存的同时将值重新排序为这样的最有效和最快的方法是什么?

+---+---+---+---+
| a | b | c | d |
+---+---+---+---+

任何建议都会很有帮助。

【问题讨论】:

  • 孔是否每次都从 [1] 开始?末尾的 abc 部分总是排序吗?
  • 孔可以从不同的偏移量开始,并且值始终以循环方式排序。
  • 两个memcpys 有性能问题吗?
  • 如果(整个数组的)元素个数是偶数,并且孔至少是缓冲区的一半(并且开头或结尾没有孔),您可以放心复制d,然后复制a … c
  • 您是只对通用 C 解决方案感兴趣,还是对特定于体系结构的解决方案感兴趣?

标签: c malloc memcpy realloc


【解决方案1】:
uint32_t *values;
if(offset == 0)
{
    values[0] = values[4];
    values[1] = values[5];
    values[2] = values[6];
    values[3] = values[7];
}
else if(offset == 1)
{
    values[3] = values[0];
    values[0] = values[5];
    values[1] = values[6];
    values[2] = values[7];
}
else if(offset == 2)
{
    values[2] = values[0];
    values[3] = values[1];
    values[0] = values[6];
    values[1] = values[7];
}
else if(offset == 3)
{
    values[3] = values[2];
    values[2] = values[1];
    values[1] = values[0];
    values[0] = values[7];
}

我知道,一个非常“愚蠢”的解决方案。
但所有内容都包含一个带有可变参数的 memmove
对我来说,在速度方面不会接近任何地方(和模......嗯,是的。)

【讨论】:

  • 我删除了我的答案,不是因为您对我的“mod”方法的批评,而是因为我在阅读“32 个八位值”时犯了一个错误,但 OP 说的是“八个 32 位值” .
【解决方案2】:

假设整个数组中有偶数个 32 位值(并且 8 是偶数),如下所示应该可以:

#include <string.h>
#include <stdint.h>

#include <stdio.h>

void re_sort_buffer(uint32_t *arr, size_t head_len, size_t hole_len, size_t tail_len) {
    memmove(arr+hole_len-head_len, arr, head_len*sizeof *arr); // see below
    memcpy(arr, arr+head_len+hole_len, tail_len*sizeof *arr);
}

int main(void) {
    uint32_t test[] = { 'b', 'c', 'd', '*', '*', '*', '*', 'a' };
    re_sort_buffer(test, 3, 4, 1);
    for(size_t i=0; i < 3+4+1; ++i) printf("'%c' ", test[i]);
    putchar('\n');
}

输出:

'a' 'b' 'c' 'd' '*' '*' '*' 'a' 

您可能想硬编码re_sort_buffer 的某些参数或传递不同的参数等。

如果 hole_len - head_lenhead_len(等效地,如果 hole_len÷2(在您的情况下 2)≥ head_len),则可以通过使用 memcpy 来避免使用 memmove;否则,我看不到更好的方法。在这种情况下,也许最好使用临时缓冲区;我认为,进一步的一切都将是特定于平台的。

HTH

【讨论】:

    【解决方案3】:

    我会编写尽可能简单的代码,使编译器易于优化。一个好的编译器(甚至是一个糟糕的编译器,TBH)比我更有机会为正在使用的平台提供最快的程序集。

    因此,例如(为简洁起见,排除了 malloc/realloc 错误检查):

    uint32_t *buf = malloc(32);
    
    // your operations here
    
    buf[3] = buf[0];
    buf[0] = buf[5];
    buf[1] = buf[6];
    buf[2] = buf[7];
    
    // optional obviously, waste of time if that 16 bytes isn't a dealbreaker
    buf = realloc(buf, 16);     
    

    【讨论】:

    • “孔可以从不同的偏移量开始”。好吧,无论如何 +1。
    • @deviantfan 问题并没有这么说(尽管我现在看到它出现在 cmets 中)。我想如果我们知道洞的位置(在这种情况下,我们可以打开它。如果我们必须搜索洞,那么需要更多信息。
    【解决方案4】:

    根据 mafso 的回答,我使用一个 memmove 和一个 memcpy 结束了这个解决方案。

    开始:

    +---+---+---+---+---+---+---+---+
    | d |   |   |   |   | a | b | c |
    +---+---+---+---+---+---+---+---+
    

    memmove 右边的第一个元素:

    +---+---+---+---+---+---+---+---+
    | d |   |   | d |   | a | b | c |
    +---+---+---+---+---+---+---+---+
    

    memcpy 结束元素到开始:

    +---+---+---+---+---+---+---+---+
    | a | b | c | d |   | a | b | c |
    +---+---+---+---+---+---+---+---+
    

    然后将realloc缩短:

    +---+---+---+---+
    | a | b | c | d |
    +---+---+---+---+
    

    简单且配置文件非常快。

    【讨论】:

    • 当然很简单,但是如果你想要一个简单的short解决方案,那就不要要求fastest ...
    • 这简单而简短,并且比您的建议更通用,因为它处理任意长度。硬编码索引虽然速度很快,但总体上是一个非常糟糕的解决方案。这实际上也非常快。
    猜你喜欢
    • 2013-05-13
    • 2012-01-19
    • 2023-03-29
    • 1970-01-01
    • 2012-11-12
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多