避免在C

时间:2018-02-13 09:56:22

标签: c

我正在阅读并试验本书的指示,

http://shop.oreilly.com/product/0636920028000.do

在本书的第6章避免malloc / free Overhead 标题下 作者建议如何在进行大量结构内存分配/解除分配时避免malloc / free开销。

以下是他编写函数的方式,

#define LIST_SIZE 10
Person *list[LIST_SIZE];

void initializeList() 
{
    int i=0;
    for(i=0; i<LIST_SIZE; i++) 
    {
        list[i] = NULL;
    }

}

Person *getPerson() 
{
    int i=0;
    for(i=0; i<LIST_SIZE; i++) 
    {

        if(list[i] != NULL) 
        {
            Person *ptr = list[i];
            list[i] = NULL;
            return ptr;
        }
    }
    Person *person = (Person*)malloc(sizeof(Person));
    return person;
}

void deallocatePerson(Person *person) 
{
    free(person->firstName);
    free(person->lastName);
    free(person->title);
}

Person *returnPerson(Person *person)
{
    int i=0;
    for(i=0; i<LIST_SIZE; i++) 
    {
    if(list[i] == NULL) 
        {
            list[i] = person;
            return person;
        }
    }
    deallocatePerson(person);
    free(person);
    return NULL;
}

我从他的代码中了解到,他创建了一个内存池数组,指向 struct person 类型,然后用NULL初始化每个数组元素。

接下来,我们将使用 getPerson 函数从池中获取内存。这个函数检查!= NULL ,我认为每次都会失败。所以它也是一样的,因为malloc和内存不会随时从池中分配。

  1. 我的理解是否正确?
  2. 这是处理开销的方法吗?
  3. 这样做的正确方法是什么?任何来源/链接都将不胜感激。

3 个答案:

答案 0 :(得分:6)

  

接下来,我们将使用getPerson函数从池中获取内存。这个函数检查我认为每次都会失败的getPerson

只要您反复继续致电getPerson,检查就会失败。但是,如果混合使用returnPersonNULL,则某些returnPerson检查会成功,因为NULL会将非struct Person值放入数组中。< / p>

此观察结果是理解该方法的关键:数组用作malloc块的小型临时存储,这些块已分配malloc,但不再使用。如果有可用的代码,您的代码将从此特殊列表中获取可用的块,而不是再次调用LIST_SIZE

在您进行数千次分配但在任何给定时间内永远不会保留超过malloc个对象的情况下,LIST_SIZE来电的数量仅限于Person *list[LIST_SIZE]

  

这是处理开销的方法吗?

这是使用旁视列表的一种变体,这是一种非常重要的优化技术,微软创建了an API for its use in driver code。一种更简单的方法是使用next作为已发布块的堆栈,即使用最后发布的块的索引而不使用循环。

另一种方法是设置这些块的链表,重用块本身的内存来存储{{1}}指针。但是,对于介绍性的嘘声,这种技术可能过于复杂。

答案 1 :(得分:2)

首先,你的作者在这里指的是开销吗?对于动态内存分配,我们调用malloc来分配内存,free来释放内存。此外,在此过程中,操作系统需要从堆中搜索可用内存并分配相同的内存。为了避免这种开销,他只是建议在应用程序加载的最开始,如果你知道可能的动态内存分配到struct的频率,你可以预先保留一个内存池,这将减少分配并且显着地释放内存开销。这在某种程度上是正确的,如果您的服务器已经运行了大量应用程序并且处理器非常繁忙,那么您可以采用这种方法。但也有缺点。在这种情况下,您已经预先从堆中保留了一个内存池。如果使用不当,将导致内存管理不善。

答案 2 :(得分:0)

我认为示例的重点可能是Person对象保存了指向附加内存的指针。我们可以从deallocatePerson函数看到结构中有三个指向字符串的指针:

void deallocatePerson(Person *person) 
{
    free(person->firstName);
    free(person->lastName);
    free(person->title);
}

这意味着要构造一个完整的Person,您需要多次调用malloc(1表示结构本身,3表示字符串)。

因此,通过保存完整的结构,包括其字符串,一个getPerson调用会将四个调用替换为malloc。这样可以节省一些执行时间。

否则,如果malloc / free内部拥有一个类似的数组或最近使用的内存块的链表,我不会感到惊讶。如果您只有free一个大小正确的内存块,则对malloc的新调用可能会很快找到非常的块。

如果Person是一个简单的结构而没有指向其他存储的指针,那么本地缓存不会 可能会提高性能(但可能会改为添加开销做线性搜索)。