Hi, all. I'm pinging this patch: https://gcc.gnu.org/ml/gcc-patches/2015-08/msg00030.html
And adding some more changes to it (they are not complete yet - so they are more like an RFC). In this patch I also try to make obstacks use the same block pool as object pools. This seems rather easy to implement because obstacks already have obstack_chunk_alloc/obstack_chunk_free callbacks, and they are easily replaced. I also change the default chunk size to memory_block_pool::block_size. This still works for object sizes greater than memory_block_pool::block_size: in this case I simply call xmalloc to allocate the requested chunk, and the deallocation function uses obstack chunk header to determine chunk's size (and depending on it calls either free or memory_block_pool::remove). The result is visible on the profile. For example, for tramp3d build _obstack_begin is the top caller of xmalloc (it constitutes 107 ms into xmalloc self time, which is 375 ms - according to one of my runs). After applying the patch it is gone from profile. This patch adds new files virtual-memory.h and virtual-memory.cc, which currently contain memory_block_pool class and related obstack functions (I plan to factor out part of ggc-page.c into this file in order to use mmap/VirtualAlloc directly, hence the name "virtual-memory"). Currently this file is linked into libcommon, and this seems somewhat wrong to me, because the driver and other command line tools don't use all memory management machinery of the compiler proper. But obstacks are used by diagnostics.c and this file is already in libcommon, so there is probably no easy way to make it use xmalloc instead of memory_block_pool::allocate. A possible solution is to create an additional file with definitions of mempool_obstack_chunk_alloc/free and use it for generators and drivers (or somehow make mempool_obstack_chunk_alloc alias to malloc). Do you have any suggestions, how this could be done in a better way? Another problem is headers. I included "virtual-memory.h" into coretypes.h, because it defines a macro gcc_obstack_init, which now uses functions from virtual-memory.h. Alternatively I could just declare those functions in coretypes.h. Would that be better? -- Regards, Mikhail Maltsev
diff --git a/gcc/Makefile.in b/gcc/Makefile.in index c1cb4ce..1b4198d 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1516,7 +1516,7 @@ OBJS = \ # Objects in libcommon.a, potentially used by all host binaries and with # no target dependencies. OBJS-libcommon = diagnostic.o diagnostic-color.o pretty-print.o intl.o \ - vec.o input.o version.o hash-table.o ggc-none.o + vec.o input.o version.o hash-table.o ggc-none.o virtual-memory.o # Objects in libcommon-target.a, used by drivers and by the core # compiler and containing target-dependent code. diff --git a/gcc/alloc-pool.c b/gcc/alloc-pool.c index f8c1351..7e25915 100644 --- a/gcc/alloc-pool.c +++ b/gcc/alloc-pool.c @@ -35,25 +35,3 @@ dump_alloc_pool_statistics (void) pool_allocator_usage.dump (ALLOC_POOL_ORIGIN); } - -/* Global singleton-like instance. */ -memory_block_pool memory_block_pool::instance; - -memory_block_pool::memory_block_pool () : m_blocks (NULL) {} - -memory_block_pool::~memory_block_pool () -{ - release (); -} - -/* Free all memory allocated by memory_block_pool. */ -void -memory_block_pool::release () -{ - while (m_blocks) - { - block_list *next = m_blocks->m_next; - XDELETEVEC (m_blocks); - m_blocks = next; - } -} diff --git a/gcc/alloc-pool.h b/gcc/alloc-pool.h index dccc41a..eccdf0d 100644 --- a/gcc/alloc-pool.h +++ b/gcc/alloc-pool.h @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #ifndef ALLOC_POOL_H #define ALLOC_POOL_H +#include "virtual-memory.h" extern void dump_alloc_pool_statistics (void); @@ -95,55 +96,6 @@ struct pool_usage: public mem_usage extern mem_alloc_description<pool_usage> pool_allocator_usage; -/* Shared pool which allows other memory pools to reuse each others' allocated - memory blocks instead of calling free/malloc again. */ -class memory_block_pool -{ -public: - /* Blocks have fixed size. This is necessary for sharing. */ - static const size_t block_size = 64 * 1024; - - memory_block_pool (); - ~memory_block_pool (); - - static inline void *allocate () ATTRIBUTE_MALLOC; - static inline void remove (void *); - void release (); - -private: - /* memory_block_pool singleton instance, defined in alloc-pool.c. */ - static memory_block_pool instance; - - struct block_list - { - block_list *m_next; - }; - - /* Free list. */ - block_list *m_blocks; -}; - -/* Allocate single block. Reuse previously returned block, if possible. */ -inline void * -memory_block_pool::allocate () -{ - if (instance.m_blocks == NULL) - return XNEWVEC (char, block_size); - - void *result = instance.m_blocks; - instance.m_blocks = instance.m_blocks->m_next; - return result; -} - -/* Return UNCAST_BLOCK to pool. */ -inline void -memory_block_pool::remove (void *uncast_block) -{ - block_list *block = reinterpret_cast<block_list *> (uncast_block); - block->m_next = instance.m_blocks; - instance.m_blocks = block; -} - #if 0 /* If a pool with custom block size is needed, one might use the following template. An instance of this template can be used as a parameter for diff --git a/gcc/coretypes.h b/gcc/coretypes.h index 17e2b40..b7b7402 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -225,12 +225,19 @@ struct basic_block_def; typedef struct basic_block_def *basic_block; typedef const struct basic_block_def *const_basic_block; -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -#define OBSTACK_CHUNK_SIZE 0 +#if !defined (GENERATOR_FILE) +# define OBSTACK_CHUNK_SIZE memory_block_pool::block_size +# define obstack_chunk_alloc mempool_obstack_chunk_alloc +# define obstack_chunk_free mempool_obstack_chunk_free +#else +# define OBSTACK_CHUNK_SIZE 0 +# define obstack_chunk_alloc xmalloc +# define obstack_chunk_free free +#endif + #define gcc_obstack_init(OBSTACK) \ obstack_specify_allocation ((OBSTACK), OBSTACK_CHUNK_SIZE, 0, \ - obstack_chunk_alloc, \ + obstack_chunk_alloc, \ obstack_chunk_free) /* enum reg_class is target specific, so it should not appear in @@ -328,6 +335,7 @@ typedef unsigned char uchar; #include "hash-set.h" #include "input.h" #include "is-a.h" +#include "virtual-memory.h" #endif /* GENERATOR_FILE && !USED_FOR_TARGET */ #endif /* coretypes.h */ diff --git a/gcc/virtual-memory.cc b/gcc/virtual-memory.cc new file mode 100644 index 0000000..69bda37 --- /dev/null +++ b/gcc/virtual-memory.cc @@ -0,0 +1,66 @@ +/* + Copyright (C) 2015 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "virtual-memory.h" +#include "obstack.h" + +/* Global singleton-like instance. */ +memory_block_pool memory_block_pool::instance; + +memory_block_pool::memory_block_pool () : m_blocks (NULL) {} + +memory_block_pool::~memory_block_pool () +{ + release (); +} + +/* Free all memory allocated by memory_block_pool. */ +void +memory_block_pool::release () +{ + while (m_blocks) + { + block_list *next = m_blocks->m_next; + XDELETEVEC (m_blocks); + m_blocks = next; + } +} + +void * +mempool_obstack_chunk_alloc (size_t size) +{ + if (size == memory_block_pool::block_size) + return memory_block_pool::allocate (); + else + return XNEWVEC (char, size); +} + +void +mempool_obstack_chunk_free (void *chunk) +{ + size_t size = reinterpret_cast<_obstack_chunk *> (chunk)->limit - + reinterpret_cast<char *>(chunk); + if (size == memory_block_pool::block_size) + memory_block_pool::remove (chunk); + else + XDELETEVEC (chunk); +} diff --git a/gcc/virtual-memory.h b/gcc/virtual-memory.h new file mode 100644 index 0000000..1d8f0bd --- /dev/null +++ b/gcc/virtual-memory.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2015 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + + +#ifndef VIRTUAL_MEMORY_H +#define VIRTUAL_MEMORY_H + +/* Shared pool which allows other memory pools to reuse each others' allocated + memory blocks instead of calling free/malloc again. */ +class memory_block_pool +{ +public: + /* Blocks have fixed size. This is necessary for sharing. */ + static const size_t block_size = 8 * 1024; + + memory_block_pool (); + ~memory_block_pool (); + + static inline void *allocate () ATTRIBUTE_MALLOC; + static inline void remove (void *); + void release (); + +private: + /* memory_block_pool singleton instance, defined in virtual-memory.cc. */ + static memory_block_pool instance; + + struct block_list + { + block_list *m_next; + }; + + /* Free list. */ + block_list *m_blocks; +}; + +/* Allocate single block. Reuse previously returned block, if possible. */ +inline void * +memory_block_pool::allocate () +{ + if (instance.m_blocks == NULL) + return XNEWVEC (char, block_size); + + void *result = instance.m_blocks; + instance.m_blocks = instance.m_blocks->m_next; + return result; +} + +/* Return UNCAST_BLOCK to pool. */ +inline void +memory_block_pool::remove (void *uncast_block) +{ + block_list *block = reinterpret_cast<block_list *> (uncast_block); + block->m_next = instance.m_blocks; + instance.m_blocks = block; +} + +extern void *mempool_obstack_chunk_alloc(size_t) ATTRIBUTE_MALLOC; +extern void mempool_obstack_chunk_free(void *); + +#endif /* VIRTUAL_MEMORY_H */