大缓冲区与大静态缓冲区,有何优劣之处?

4
考虑以下代码。
在1000次连续执行中,DoSomething1()比DoSomething2()更快吗?如果我调用DoSomething1() 1000次,那么它应该比调用DoSomething2() 1000次更快。
将所有大缓冲区都设置为静态的有什么不利之处吗?
#define MAX_BUFFER_LENGTH 1024*5 
void DoSomething1()
{
    static char buf[MAX_BUFFER_LENGTH] ; 
    memset( buf, 0, MAX_BUFFER_LENGTH );
}

void DoSomething2()
{
    char buf[MAX_BUFFER_LENGTH] ; 
    memset( buf, 0, MAX_BUFFER_LENGTH );
}

感谢您的时间。
6个回答

8

静态缓冲区的缺点:

  • 如果需要线程安全,则使用静态缓冲区可能不是一个好主意。
  • 内存直到程序结束才会被释放,因此会增加内存消耗。

静态缓冲区的优点:

  • 静态缓冲区的分配次数更少。不需要每次在堆栈上进行分配。
  • 使用静态缓冲区时,由于分配过高而导致堆栈溢出的概率较小。

2
栈分配很快,所以我不会担心那个。然而,堆栈溢出是我非常担心的事情。 - David Thornley

6

如果你在VC++编译器中启用了/GS,即启用缓冲区溢出的安全检查(/GS默认开启),那么堆栈分配会稍微费一些。实际上,你应该对这两种选项进行性能测试,看哪个更快。可能静态内存中的缓存局部性与堆栈中的差异可能会有所不同。

这是非静态版本,使用VC++编译器,并启用/O2。

_main   PROC                        ; COMDAT
; Line 5
    mov eax, 5124               ; 00001404H
    call    __chkstk
    mov eax, DWORD PTR ___security_cookie
    xor eax, esp
    mov DWORD PTR __$ArrayPad$[esp+5124], eax
; Line 7
    push    5120                    ; 00001400H
    lea eax, DWORD PTR _buf$[esp+5128]
    push    0
    push    eax
    call    _memset
; Line 9
    mov ecx, DWORD PTR __$ArrayPad$[esp+5136]
    movsx   eax, BYTE PTR _buf$[esp+5139]
    add esp, 12                 ; 0000000cH
    xor ecx, esp
    call    @__security_check_cookie@4
    add esp, 5124               ; 00001404H
    ret 0
_main   ENDP
_TEXT   ENDS

这里是静态版本

;   COMDAT _main
_TEXT   SEGMENT
_main   PROC                        ; COMDAT
; Line 7
    push    5120                    ; 00001400H
    push    0
    push    OFFSET ?buf@?1??main@@9@4PADA
    call    _memset
; Line 8
    movsx   eax, BYTE PTR ?buf@?1??main@@9@4PADA+3
    add esp, 12                 ; 0000000cH
; Line 9
    ret 0
_main   ENDP
_TEXT   ENDS
END

4
两者之间几乎没有速度差异。在堆栈上分配缓冲区非常快 - 它只是通过减少堆栈指针的值来完成。但是,如果您在堆栈上分配了一个非常大的缓冲区,则有可能会溢出堆栈并导致segfault /访问冲突。相反,如果您有很多静态缓冲区,则会显着增加程序的工作集大小,尽管如果具有良好的引用局部性,则会在某种程度上减轻这种情况。
另一个主要区别是,堆栈缓冲区是线程安全和可重入的,而静态缓冲区既不是线程安全也不是可重入的。

3

我是唯一一个在开发多线程软件的人吗?如果你不想陷入频繁的锁定和解锁,那么在这种情况下,静态缓冲区绝对是不可取的。


2
您可以考虑将代码放入一个类中。例如像这样:
const MAX_BUFFER_LENGTH = 1024*5; 
class DoSomethingEngine {
  private:
    char *buffer;
  public:
    DoSomethingEngine() {
      buffer = new buffer[MAX_BUFFER_LENGTH];
    }
    virtual ~DoSomethingEngine() {
       free(buffer);
    }
    void DoItNow() {
       memset(buffer, 0, MAX_BUFFER_LENGTH);
       ...
    }
}

如果每个线程只分配自己的引擎,那么这是线程安全的。这样可以避免在堆栈上分配大量内存。在堆上分配内存会带来一些开销,但如果您多次重复使用该类的实例,则此开销可以忽略不计。


0

正如其他人所说,堆栈分配非常快,不需要每次重新分配的速度提升对于更复杂的对象(例如ArrayList或HashTable,在泛型世界中现在是List<>和Dictionary<,>)可能更大,因为每次都需要运行构造代码。此外,如果容量设置不正确,则每当容器达到容量并需要分配新内存并将内容从旧内存复制到新内存时,就会发生不必要的重新分配。因此,我经常使用工作中的List<>对象,允许它们按需增长,并通过调用Clear()来重用它们-这样可以保留已分配的内存/容量。但是,如果偶尔或仅一次发生了分配大量内存的错误调用,您应该注意内存泄漏的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接