2011-09-16 8 views
56

Per quanto riguarda i rispettivi standard linguistici, C offre allocazione dinamica della memoria solo tramite la famiglia malloc(), mentre in C++ la forma di allocazione più comune viene eseguita da ::operator new(). Il malloc in stile C è anche disponibile in C++, e molti esempi di "primo allocatore del bambino" lo usano come funzione di allocazione principale, ma sono curioso di sapere come i compilatori contemporanei implementino l'operatore di produzione attuale, nuovo.L'allocazione della memoria dinamica differisce in C e C++ nelle implementazioni più diffuse?

È solo un involucro sottile attorno a malloc() o è implementato in modo fondamentalmente diverso a causa del comportamento di allocazione della memoria piuttosto diverso di un tipico programma C++ rispetto a un tipico programma C?

[Edit: Credo che la differenza principale è di solito descritta come segue: un programma di C ha meno, più grandi, assegnazioni di lunga durata, mentre un programma C++ ha molti, piccolo, assegnazioni di breve durata. Sentitevi liberi di intervenire se questo è sbagliato, ma sembra che uno trarrebbe beneficio dal tenerne conto.]

Per un compilatore come GCC, sarebbe facile avere solo un'implementazione di allocazione core singola e usarlo per tutti i rilevanti lingue, quindi mi chiedo se ci sono differenze nei dettagli che cercano di ottimizzare le prestazioni di allocazione risultanti in ciascuna lingua.


Update: Grazie per tutte le grandi risposte! Sembra che in GCC questo sia completamente risolto da ptmalloc e che MSVC utilizzi anche lo malloc al centro. Qualcuno sa come viene implementato MSVC-malloc?

+1

(Sarei felice di conoscere i compilatori non GCC, se qualcuno dovesse avere qualche approfondimento.) –

risposta

48

Ecco l'implementazione utilizzata g++ 4.6.1:

_GLIBCXX_WEAK_DEFINITION void * 
operator new (std::size_t sz) throw (std::bad_alloc) 
{ 
    void *p; 

    /* malloc (0) is unpredictable; avoid it. */ 
    if (sz == 0) 
    sz = 1; 
    p = (void *) malloc (sz); 
    while (p == 0) 
    { 
     new_handler handler = __new_handler; 
     if (! handler) 
#ifdef __EXCEPTIONS 
     throw bad_alloc(); 
#else 
     std::abort(); 
#endif 
     handler(); 
     p = (void *) malloc (sz); 
    } 

    return p; 
} 

Questo si trova nella libstdc++-v3/libsupc++/new_op.cc all'interno del g distribuzione sorgente ++.

Come si può vedere, è un involucro piuttosto sottile intorno a malloc.

modificare Su molti sistemi, è possibile mettere a punto il comportamento di malloc, tipicamente chiamando mallopt o delle variabili di ambiente. Ecco uno article che discute alcune funzionalità disponibili su Linux.

According to Wikipedia, glibc versioni 2.3 + utilizzano una versione modificata del allocatore chiamato ptmalloc, che di per sé è un derivato del dlmalloc creazione di Doug Lea. È interessante notare che, in un article about dlmalloc Doug Lea dà la seguente prospettiva (sottolineatura mia):

ho scritto la prima versione del allocatore dopo aver scritto alcuni programmi C++ che si basavano quasi esclusivamente sulla allocazione dinamica della memoria. Ho scoperto che funzionavano molto più lentamente e/o con molto più totale consumo di memoria di quanto mi aspettassi. Ciò era dovuto alle caratteristiche degli allocatori di memoria sui sistemi su cui stavo correndo (principalmente le versioni attuali di SunOs e BSD). Per contrastare lo , inizialmente ho scritto un numero di allocatori speciali in C++, , normalmente sovraccaricando l'operatore new per varie classi. Alcuni di sono descritti in un documento sulle tecniche di allocazione C++ che è stato adattato nell'articolo del report C++ del 1989 Alcune tecniche di allocazione dello storage per le classi contenitore.

Tuttavia, ben presto mi sono reso conto che la costruzione di un allocatore speciale per ogni nuova classe che tendeva ad essere allocato in modo dinamico e fortemente utilizzato è stato non è una buona strategia per la costruzione di tipi di programmazione classi di supporto general-purpose che stavo scrivendo al tempo. (Dal 1986 al 1991, sono stato l'autore principale di libg ++, libreria GNU C++.) Era necessaria una più ampia soluzione - di scrivere un allocatore che era abbastanza buono in condizioni normali C++ e C carichi modo che i programmatori non sarebbe tentato di scrivere allocatori speciali se non con condizioni speciali .

Questo articolo presenta una descrizione di alcuni dei principali obiettivi di progettazione, gli algoritmi e considerazioni sull'implementazione per questo allocatore.

+0

Grazie mille! Che interessante (e anche leggermente deludente). Forse 'malloc()' è già così buono che gestisce la maggior parte delle situazioni con prestazioni soddisfacenti. –

+1

@Kerrek SB: Per essere sincero, la mia intuizione non suggerisce che C e C++ abbiano schemi di allocazione molto diversi. Detto questo, la mia intuizione potrebbe ovviamente essere sbagliata. :-) – NPE

+0

@Kerrek: performnce ?? Se si elimina la "confusione", nel caso in cui malloc non fallisse, il codice sopra diventa semplicemente ... p = malloc (...); se (! p) {} restituisce p; Considerare il tempo impiegato dal sistema operativo nell'esecuzione di malloc, non vedo come se (! P) (nell'assembler è solo un'introduzione JZ) possa cambiare performance! –

15

Nella maggior parte delle implementazioni operator new() basta chiamare malloc(). In effetti anche The Standard suggests that as a default stratege. Naturalmente è possibile implementare il proprio operator new, di solito per una classe se si desidera ottenere prestazioni migliori, ma l'impostazione predefinita di solito è solo chiamando malloc().

+0

In realtà, dove troverei il codice sorgente per quello in GCC? Questo non fa parte delle intestazioni. È nelle fonti libstdC++ da qualche parte? –

+0

@Kerrek SB: Una prova indiretta è che 'free (new char)' sembra funzionare correttamente. – sharptooth

+0

molto birichino :-) È curioso però che due linguaggi piuttosto diversi possano essere soddisfatti dagli stessi algoritmi di allocazione. Forse questo parla solo della qualità del design di 'malloc()' ... –

2

Non è una questione di prestazioni: pA = new A ha un effetto collaterale diverso pA = (A*)malloc(sizeof(A));

Nel secondo, il costruttore di A non viene chiamato. di arrivare ad una stesso effetto si dovrebbe fare

pA = (A*)malloc(sizeof(A)); 
new(pA)A(); 

dove nuovo è la "nuova collocazione" ...

void* operator new(size_t sz, void* place) 
{ return place; } 
+7

Non sto chiedendo dell'espressione 'nuova'. Sono ben consapevole dei concetti di base del linguaggio C++. Sto chiedendo in particolare su ':: operator new()' (l'impostazione predefinita, non quella di posizionamento), che è la funzione di allocazione utilizzata dall'espressione 'new'. –

12

glibc nuovo operatore è un wrapper sottile intorno malloc. E glibc malloc utilizza strategie diverse per richieste di allocazione di dimensioni diverse. Puoi vedere l'implementazione, o almeno i commenti here.

Ecco un estratto dai commenti in malloc.c:

/* 
47 This is not the fastest, most space-conserving, most portable, or 
48 most tunable malloc ever written. However it is among the fastest 
49 while also being among the most space-conserving, portable and tunable. 
50 Consistent balance across these factors results in a good general-purpose 
51 allocator for malloc-intensive programs. 
52 
53 The main properties of the algorithms are: 
54 * For large (>= 512 bytes) requests, it is a pure best-fit allocator, 
55  with ties normally decided via FIFO (i.e. least recently used). 
56 * For small (<= 64 bytes by default) requests, it is a caching 
57  allocator, that maintains pools of quickly recycled chunks. 
58 * In between, and for combinations of large and small requests, it does 
59  the best it can trying to meet both goals at once. 
60 * For very large requests (>= 128KB by default), it relies on system 
61  memory mapping facilities, if supported. 
*/ 
+0

Neat, grazie! –

10

su Visual C++, entrare in un espressione new mi porta a questo frammento in new.cpp:

#include <cstdlib> 
#include <new> 

_C_LIB_DECL 
int __cdecl _callnewh(size_t size) _THROW1(_STD bad_alloc); 
_END_C_LIB_DECL 

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) 
     {  // try to allocate size bytes 
     void *p; 
     while ((p = malloc(size)) == 0) 
       if (_callnewh(size) == 0) 
       {  // report no memory 
       static const std::bad_alloc nomem; 
       _RAISE(nomem); 
       } 

     return (p); 
     } 

Così VC++ s 'new anche termina la chiamata malloc().

+1

Grande, grazie! Mi chiedo quale 'malloc()' VC usa. –

+0

L'implementazione malloc di VC è in realtà un involucro sottile attorno a RtlAllocateHeap in ntdll.dll. – newgre

Problemi correlati