On Wed, 20 Nov 2019, Jan Hubicka wrote: > Hi, > I have noticed that for Firefox around 1GB of peak memory use goes into > the fact that we never free memory_block_pool::freelist. > > This patch adds memory_block_pool::trim which reduces freelist to a given > size. It is called from ggc_collect which is a convenient place to return > heap allocations too and fully freeed prior forking in ggc_collect. > > I originaly was freeing block directly in memory_block_pool::release > but that makes it non-leaf function which prevents optimization. > So I decided to go this way we get tiny bit better code > given that we already have ggc_collect that is conveninet place > to do such a bookeeping. > > Bootstrapped/regtested x86_64-linux, tested on Firefox build, OK?
Huh, I think given that trimming happens explicitely the whole overhead of counting the number of freelist entries plus adding a prev member to the block list is useless overhead. In trim just do block_list **tailp = &m_blocks; for (unsigned cnt = freelist_size; cnt != 0 && *tailp; --cnt) tailp = &(*tailp)->next; while (*tailp) free blocks starting from here the list walk is O(constant) and the above would just mean adding a single method rather than cahnges all over the place. Richard. > Honza > > * memory-block.h (memory_block_pool::freelist): New constant. > (memory_block_pool::clear_free_list): Rename to ... > (memory_block_pool::reduce_free_list): ... this. > (memory_block_pool::trim): New function. > (memory_block_pool::block_list): Add m_prev. > (memory_block_pool::m_num_blocks): New field. > (memory_block_pool::m_block_end): New field. > (memory_block_pool::allocate): Maintain m_num_blocks and m_blocks_end. > (memory_block_pool::release): Likewise. > * memory-block.cc (memory_block_pool::memory_block_pool): Initialize > new fields. > (memory_block_pool::clear_free_list): Rename to ... > (memory_block_pool::reduce_free_list): ... this one; free from end > and add NUM parameter. > (memory_block_pool::trim): New. > * ggc-page.c (ggc_collect): Call memory_block_pool::trim. > > * lto.c: Call memory_block_pool::trim. > Index: memory-block.h > =================================================================== > --- memory-block.h (revision 278464) > +++ memory-block.h (working copy) > @@ -28,12 +28,15 @@ class memory_block_pool > public: > /* Blocks have fixed size. This is necessary for sharing. */ > static const size_t block_size = 64 * 1024; > + /* Number of blocks we keep in the freelists. */ > + static const size_t freelist_size = 1024 * 1024 / block_size; > > memory_block_pool (); > > static inline void *allocate () ATTRIBUTE_MALLOC; > static inline void release (void *); > - void clear_free_list (); > + static void trim (int nblocks = freelist_size); > + void reduce_free_list (int); > > private: > /* memory_block_pool singleton instance, defined in memory-block.cc. */ > @@ -42,10 +45,13 @@ private: > struct block_list > { > block_list *m_next; > + block_list *m_prev; > }; > > /* Free list. */ > block_list *m_blocks; > + block_list *m_blocks_end; > + int m_num_blocks; > }; > > /* Allocate a single block. Reuse a previously returned block, if possible. > */ > @@ -57,6 +63,9 @@ memory_block_pool::allocate () > > void *result = instance.m_blocks; > instance.m_blocks = instance.m_blocks->m_next; > + instance.m_num_blocks--; > + if (!instance.m_blocks) > + instance.m_blocks_end = NULL; > VALGRIND_DISCARD (VALGRIND_MAKE_MEM_UNDEFINED (result, block_size)); > return result; > } > @@ -67,7 +76,12 @@ memory_block_pool::release (void *uncast > { > block_list *block = new (uncast_block) block_list; > block->m_next = instance.m_blocks; > + if (instance.m_blocks) > + instance.m_blocks->m_prev = block; > + else > + instance.m_blocks_end = block; > instance.m_blocks = block; > + instance.m_num_blocks++; > > VALGRIND_DISCARD (VALGRIND_MAKE_MEM_NOACCESS ((char *)uncast_block > + sizeof (block_list), > Index: memory-block.cc > =================================================================== > --- memory-block.cc (revision 278464) > +++ memory-block.cc (working copy) > @@ -26,18 +27,27 @@ along with GCC; see the file COPYING3. > /* Global singleton-like instance. */ > memory_block_pool memory_block_pool::instance; > > -memory_block_pool::memory_block_pool () : m_blocks (NULL) {} > +/* Default constructor. */ > +memory_block_pool::memory_block_pool () > + : m_blocks (NULL), m_blocks_end (NULL), m_num_blocks (0) > +{ > +} > > -/* Return all blocks from free list to the OS. */ > +/* Reduce free list to NUM blocks. */ > void > -memory_block_pool::clear_free_list () > +memory_block_pool::reduce_free_list (int num) > { > - while (m_blocks) > + gcc_checking_assert (num >= 0); > + while (m_num_blocks > num) > { > - block_list *next = m_blocks->m_next; > - XDELETEVEC (m_blocks); > - m_blocks = next; > + block_list *prev = m_blocks_end->m_prev; > + XDELETEVEC (m_blocks_end); > + m_blocks_end = prev; > + prev->m_next = NULL; > + m_num_blocks--; > } > + if (!m_num_blocks) > + m_blocks = m_blocks_end = 0; > } > > /* Allocate a chunk for obstack. Use the pool if requested chunk size > matches > @@ -62,3 +72,10 @@ mempool_obstack_chunk_free (void *chunk) > else > XDELETEVEC (chunk); > } > + > +/* Return allocated memory back to malloc (and to system). */ > +void > +memory_block_pool::trim (int num) > +{ > + instance.reduce_free_list (num); > +} > Index: ggc-page.c > =================================================================== > --- ggc-page.c (revision 278464) > +++ ggc-page.c (working copy) > @@ -2186,6 +2186,9 @@ ggc_collect (void) > float allocated_last_gc = > MAX (G.allocated_last_gc, (size_t)param_ggc_min_heapsize * 1024); > > + /* It is a also good time to get memory block pool into limits. */ > + memory_block_pool::trim (); > + > float min_expand = allocated_last_gc * param_ggc_min_expand / 100; > if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect) > return; > Index: lto/lto.c > =================================================================== > --- lto/lto.c (revision 278464) > +++ lto/lto.c (working copy) > @@ -387,6 +387,7 @@ lto_wpa_write_files (void) > temp_priority.safe_push (part->insns); > temp_filenames.safe_push (xstrdup (temp_filename)); > } > + memory_block_pool::trim (0); > > for (int set = 0; set < MAX (lto_parallelism, 1); set++) > { > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)