* Aligned blocks management: obsolete? @ 2011-12-12 8:11 Dmitry Antipov 2011-12-12 13:13 ` Eli Zaretskii 2011-12-12 18:38 ` Stefan Monnier 0 siblings, 2 replies; 25+ messages in thread From: Dmitry Antipov @ 2011-12-12 8:11 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 304 bytes --] Is it still required to maintain the cache of aligned blocks to workaround poor malloc behavior? I believe an attached example should perform well (i.e. allocate ~4K blocks without ~4K holes between them) on top of any non-ancient glibc (no ideas about other system malloc implementations, BTW). Dmitry [-- Attachment #2: aligned-alloc.c --] [-- Type: text/plain, Size: 745 bytes --] #include <stdio.h> #include <stdlib.h> #define BLOCKSZ 4096 int main (int argc, char *argv[]) { int i, j; void *p; /* make some chaos in the heap */ for (i = 0; i < 1000; i++) for (j = 0; j < 1024; j++) { p = malloc (j); if (((i + j) & 7) == 0) free (p); } /* test */ for (i = 0; i < 16; i++) if (!posix_memalign (&p, BLOCKSZ, BLOCKSZ - sizeof (long))) printf ("%p\n", p); printf ("--\n"); /* make even more chaos with larger chunks */ for (i = 0; i < 1000; i++) { p = malloc (i * 1000); if ((i & 7) == 0) free (p); } /* test again */ for (i = 0; i < 16; i++) if (!posix_memalign (&p, BLOCKSZ, BLOCKSZ - sizeof (long))) printf ("%p\n", p); return 0; } ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2011-12-12 8:11 Aligned blocks management: obsolete? Dmitry Antipov @ 2011-12-12 13:13 ` Eli Zaretskii 2011-12-12 14:07 ` Dmitry Antipov 2011-12-12 18:38 ` Stefan Monnier 1 sibling, 1 reply; 25+ messages in thread From: Eli Zaretskii @ 2011-12-12 13:13 UTC (permalink / raw) To: Dmitry Antipov; +Cc: emacs-devel > Date: Mon, 12 Dec 2011 12:11:48 +0400 > From: Dmitry Antipov <dmantipov@yandex.ru> > > Is it still required to maintain the cache of aligned blocks > to workaround poor malloc behavior? Can you please tell what you mean by that? Are you alluding to lisp_align_malloc and its subroutines, or to something else? > I believe an attached > example should perform well (i.e. allocate ~4K blocks without > ~4K holes between them) on top of any non-ancient glibc (no > ideas about other system malloc implementations, BTW). I hope Emacs is not on its way to become a glibc-only project. Quite a few supported platforms don't use glibc: Cygwin, *BSD (AFAIK), Windows. Are platforms that use gmalloc.c, with or without ralloc.c, OK in this regard? If not, what feature(s) are missing that are present in glibc? ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2011-12-12 13:13 ` Eli Zaretskii @ 2011-12-12 14:07 ` Dmitry Antipov 2011-12-12 18:11 ` Eli Zaretskii 2011-12-12 18:27 ` Paul Eggert 0 siblings, 2 replies; 25+ messages in thread From: Dmitry Antipov @ 2011-12-12 14:07 UTC (permalink / raw) To: emacs-devel On 12/12/2011 05:13 PM, Eli Zaretskii wrote: > Can you please tell what you mean by that? Are you alluding to > lisp_align_malloc and its subroutines, or to something else? Yes. I'm just curious about comment above lisp_align_malloc - the comment references glibc-2.3.2, which was released ~8 years ago :-). > I hope Emacs is not on its way to become a glibc-only project. Quite > a few supported platforms don't use glibc: Cygwin, *BSD (AFAIK), > Windows. That's why I'm asking for. I suppose that any non-ancient glibc malloc doesn't require such a glitch in lisp_align_malloc any more - but I have no ideas about malloc implementation on other supported systems. Dmitry ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2011-12-12 14:07 ` Dmitry Antipov @ 2011-12-12 18:11 ` Eli Zaretskii 2011-12-12 18:27 ` Paul Eggert 1 sibling, 0 replies; 25+ messages in thread From: Eli Zaretskii @ 2011-12-12 18:11 UTC (permalink / raw) To: Dmitry Antipov; +Cc: emacs-devel > Date: Mon, 12 Dec 2011 18:07:15 +0400 > From: Dmitry Antipov <dmantipov@yandex.ru> > > > Can you please tell what you mean by that? Are you alluding to > > lisp_align_malloc and its subroutines, or to something else? > > Yes. I'm just curious about comment above lisp_align_malloc - > the comment references glibc-2.3.2, which was released ~8 years ago :-). > > > I hope Emacs is not on its way to become a glibc-only project. Quite > > a few supported platforms don't use glibc: Cygwin, *BSD (AFAIK), > > Windows. > > That's why I'm asking for. I suppose that any non-ancient glibc malloc > doesn't require such a glitch in lisp_align_malloc any more - but > I have no ideas about malloc implementation on other supported systems. Can you formulate the misfeatures which lisp_align_malloc protects against? Then perhaps it would be possible to see whether gmalloc and ralloc solve them. TIA ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2011-12-12 14:07 ` Dmitry Antipov 2011-12-12 18:11 ` Eli Zaretskii @ 2011-12-12 18:27 ` Paul Eggert 2012-06-19 16:51 ` Dmitry Antipov 1 sibling, 1 reply; 25+ messages in thread From: Paul Eggert @ 2011-12-12 18:27 UTC (permalink / raw) To: Dmitry Antipov; +Cc: emacs-devel On 12/12/11 06:07, Dmitry Antipov wrote: > I suppose that any non-ancient glibc malloc > doesn't require such a glitch in lisp_align_malloc any more This sounds correct to me as well, though I can't cite chapter and verse offhand. Certainly the posix_memalign implementation has mutated significantly since glibc 2.3.2 came out in 2003, and any performance measurements based on 2.3.2 are suspect now. More generally, if some cruft was put into alloc.c long ago for performance reasons, and if we can no longer demonstrate that the cruft improves performance significantly on currently-used platforms, then surely it's OK to remove it after the feature freeze is over. Cruft like that has a real maintenance cost, and there's no point keeping it if it doesn't actually have a performance benefit. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2011-12-12 18:27 ` Paul Eggert @ 2012-06-19 16:51 ` Dmitry Antipov 2012-06-19 17:13 ` Eli Zaretskii ` (2 more replies) 0 siblings, 3 replies; 25+ messages in thread From: Dmitry Antipov @ 2012-06-19 16:51 UTC (permalink / raw) To: emacs-devel; +Cc: Paul Eggert [-- Attachment #1: Type: text/plain, Size: 1183 bytes --] [some stuff from old discussion below] On 12/12/2011 10:27 PM, Paul Eggert wrote: > On 12/12/11 06:07, Dmitry Antipov wrote: >> I suppose that any non-ancient glibc malloc >> doesn't require such a glitch in lisp_align_malloc any more > > This sounds correct to me as well, though I can't cite > chapter and verse offhand. Certainly the posix_memalign > implementation has mutated significantly since glibc 2.3.2 > came out in 2003, and any performance measurements > based on 2.3.2 are suspect now. > > More generally, if some cruft was put into alloc.c long ago > for performance reasons, and if we can no longer demonstrate > that the cruft improves performance significantly > on currently-used platforms, then surely it's OK to remove it > after the feature freeze is over. Cruft like that has a real > maintenance cost, and there's no point keeping it > if it doesn't actually have a performance benefit. I believe this becomes more and more actual because more and more of ancient systems with poor malloc implementations falls into their graves; this cleanup assumes that every malloc implementation has reasonably efficient posix_memalign or memalign at least. Dmitry [-- Attachment #2: lisp_align_malloc_cleanup.patch --] [-- Type: text/plain, Size: 12530 bytes --] === modified file 'src/alloc.c' --- src/alloc.c 2012-06-16 12:24:15 +0000 +++ src/alloc.c 2012-06-19 16:46:26 +0000 @@ -309,9 +309,6 @@ MEM_TYPE_VECTOR_BLOCK }; -static void *lisp_malloc (size_t, enum mem_type); - - #if GC_MARK_STACK || defined GC_MALLOC_CHECK #if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES @@ -888,19 +885,37 @@ return Qnil; } +#if ! USE_LSB_TAG + +/* Used to catch invalid address when debugging. */ + +void *lisp_malloc_loser EXTERNALLY_VISIBLE; + +/* Nonzero if the memory at ADDR can be + addressed thru a Lisp object's pointer. */ + +static inline void +verify_address (char *addr) +{ + Lisp_Object obj; + + XSETCONS (obj, addr); + if ((char *) XCONS (obj) == addr) + return 1; + lisp_malloc_loser = addr; + return 0; +} + +#endif /* not USE_LSB_TAG */ /* Like malloc but used for allocating Lisp data. NBYTES is the number of bytes to allocate, TYPE describes the intended use of the allocated memory block (for strings, for conses, ...). */ -#if ! USE_LSB_TAG -void *lisp_malloc_loser EXTERNALLY_VISIBLE; -#endif - static void * lisp_malloc (size_t nbytes, enum mem_type type) { - register void *val; + void *val; MALLOC_BLOCK_INPUT; @@ -908,24 +923,33 @@ allocated_mem_type = type; #endif +#ifdef DOUG_LEA_MALLOC + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); +#endif val = (void *) malloc (nbytes); +#ifdef DOUG_LEA_MALLOC + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); +#endif + + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } #if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, - that's equivalent to running out of memory. */ - if (val && type != MEM_TYPE_NON_LISP) + if (val && type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) { - Lisp_Object tem; - XSETCONS (tem, (char *) val + nbytes - 1); - if ((char *) XCONS (tem) != (char *) val + nbytes - 1) - { - lisp_malloc_loser = val; - free (val); - val = 0; - } + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); } -#endif +#endif /* not USE_LSB_TAG */ #if GC_MARK_STACK && !defined GC_MALLOC_CHECK if (val && type != MEM_TYPE_NON_LISP) @@ -933,116 +957,33 @@ #endif MALLOC_UNBLOCK_INPUT; - if (!val && nbytes) - memory_full (nbytes); return val; } -/* Free BLOCK. This must be called to free memory allocated with a - call to lisp_malloc. */ - -static void -lisp_free (void *block) -{ - MALLOC_BLOCK_INPUT; - free (block); -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK - mem_delete (mem_find (block)); -#endif - MALLOC_UNBLOCK_INPUT; -} - -/***** Allocation of aligned blocks of memory to store Lisp data. *****/ - -/* The entry point is lisp_align_malloc which returns blocks of at most - BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */ - -#if defined (HAVE_POSIX_MEMALIGN) && defined (SYSTEM_MALLOC) -#define USE_POSIX_MEMALIGN 1 -#endif +/* Allocation of aligned blocks. We assume that malloc implementation + provides posix_memalign or (obsolete) memalign at least. */ /* BLOCK_ALIGN has to be a power of 2. */ + #define BLOCK_ALIGN (1 << 10) -/* Padding to leave at the end of a malloc'd block. This is to give - malloc a chance to minimize the amount of memory wasted to alignment. - It should be tuned to the particular malloc library used. - On glibc-2.3.2, malloc never tries to align, so a padding of 0 is best. - posix_memalign on the other hand would ideally prefer a value of 4 - because otherwise, there's 1020 bytes wasted between each ablocks. - In Emacs, testing shows that those 1020 can most of the time be - efficiently used by malloc to place other objects, so a value of 0 can - still preferable unless you have a lot of aligned blocks and virtually - nothing else. */ -#define BLOCK_PADDING 0 -#define BLOCK_BYTES \ - (BLOCK_ALIGN - sizeof (struct ablocks *) - BLOCK_PADDING) - -/* Internal data structures and constants. */ - -#define ABLOCKS_SIZE 16 - -/* An aligned block of memory. */ -struct ablock -{ - union - { - char payload[BLOCK_BYTES]; - struct ablock *next_free; - } x; - /* `abase' is the aligned base of the ablocks. */ - /* It is overloaded to hold the virtual `busy' field that counts - the number of used ablock in the parent ablocks. - The first ablock has the `busy' field, the others have the `abase' - field. To tell the difference, we assume that pointers will have - integer values larger than 2 * ABLOCKS_SIZE. The lowest bit of `busy' - is used to tell whether the real base of the parent ablocks is `abase' - (if not, the word before the first ablock holds a pointer to the - real base). */ - struct ablocks *abase; - /* The padding of all but the last ablock is unused. The padding of - the last ablock in an ablocks is not allocated. */ -#if BLOCK_PADDING - char padding[BLOCK_PADDING]; -#endif -}; - -/* A bunch of consecutive aligned blocks. */ -struct ablocks -{ - struct ablock blocks[ABLOCKS_SIZE]; -}; - -/* Size of the block requested from malloc or posix_memalign. */ -#define ABLOCKS_BYTES (sizeof (struct ablocks) - BLOCK_PADDING) - -#define ABLOCK_ABASE(block) \ - (((uintptr_t) (block)->abase) <= (1 + 2 * ABLOCKS_SIZE) \ - ? (struct ablocks *)(block) \ - : (block)->abase) - -/* Virtual `busy' field. */ -#define ABLOCKS_BUSY(abase) ((abase)->blocks[0].abase) - -/* Pointer to the (not necessarily aligned) malloc block. */ -#ifdef USE_POSIX_MEMALIGN -#define ABLOCKS_BASE(abase) (abase) -#else -#define ABLOCKS_BASE(abase) \ - (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void**)abase)[-1]) -#endif - -/* The list of free ablock. */ -static struct ablock *free_ablock; - -/* Allocate an aligned block of nbytes. - Alignment is on a multiple of BLOCK_ALIGN and `nbytes' has to be - smaller or equal to BLOCK_BYTES. */ +/* Padding to leave at the end of a malloc'd block. This should help + the malloc implementation to allocate aligned blocks consecutively. + FIXME: tuned to fit glibc malloc, may be suboptimal for others. */ + +#define BLOCK_PADDING sizeof (long) + +/* Maximum amount of memory in aligned block. */ + +#define BLOCK_BYTES (BLOCK_ALIGN - BLOCK_PADDING) + +/* Like lisp_malloc, but allocates aligned block of at + most BLOCK_BYTES aligned on a BLOCK_ALIGN boundary. */ + static void * lisp_align_malloc (size_t nbytes, enum mem_type type) { - void *base, *val; - struct ablocks *abase; + void *val; eassert (nbytes <= BLOCK_BYTES); @@ -1052,86 +993,40 @@ allocated_mem_type = type; #endif - if (!free_ablock) - { - int i; - intptr_t aligned; /* int gets warning casting to 64-bit pointer. */ - #ifdef DOUG_LEA_MALLOC - /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed - because mapped region contents are not preserved in - a dumped Emacs. */ - mallopt (M_MMAP_MAX, 0); + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); #endif -#ifdef USE_POSIX_MEMALIGN - { - int err = posix_memalign (&base, BLOCK_ALIGN, ABLOCKS_BYTES); - if (err) - base = NULL; - abase = base; - } +#ifdef HAVE_POSIX_MEMALIGN + if (posix_memalign (&val, BLOCK_ALIGN, nbytes)) + val = NULL; #else - base = malloc (ABLOCKS_BYTES); - abase = ALIGN (base, BLOCK_ALIGN); + val = memalign (BLOCK_ALIGN, nbytes); #endif - if (base == 0) - { - MALLOC_UNBLOCK_INPUT; - memory_full (ABLOCKS_BYTES); - } - - aligned = (base == abase); - if (!aligned) - ((void**)abase)[-1] = base; - #ifdef DOUG_LEA_MALLOC - /* Back to a reasonable maximum of mmap'ed areas. */ - mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); #endif + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } + #if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, that's equivalent to - running out of memory. */ - if (type != MEM_TYPE_NON_LISP) - { - Lisp_Object tem; - char *end = (char *) base + ABLOCKS_BYTES - 1; - XSETCONS (tem, end); - if ((char *) XCONS (tem) != end) - { - lisp_malloc_loser = base; - free (base); - MALLOC_UNBLOCK_INPUT; - memory_full (SIZE_MAX); - } - } -#endif - - /* Initialize the blocks and put them on the free list. - If `base' was not properly aligned, we can't use the last block. */ - for (i = 0; i < (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1); i++) - { - abase->blocks[i].abase = abase; - abase->blocks[i].x.next_free = free_ablock; - free_ablock = &abase->blocks[i]; - } - ABLOCKS_BUSY (abase) = (struct ablocks *) aligned; - - eassert (0 == ((uintptr_t) abase) % BLOCK_ALIGN); - eassert (ABLOCK_ABASE (&abase->blocks[3]) == abase); /* 3 is arbitrary */ - eassert (ABLOCK_ABASE (&abase->blocks[0]) == abase); - eassert (ABLOCKS_BASE (abase) == base); - eassert (aligned == (intptr_t) ABLOCKS_BUSY (abase)); + if (type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) + { + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); } - - abase = ABLOCK_ABASE (free_ablock); - ABLOCKS_BUSY (abase) = - (struct ablocks *) (2 + (intptr_t) ABLOCKS_BUSY (abase)); - val = free_ablock; - free_ablock = free_ablock->x.next_free; +#endif /* not USE_LSB_TAG */ #if GC_MARK_STACK && !defined GC_MALLOC_CHECK if (type != MEM_TYPE_NON_LISP) @@ -1139,51 +1034,21 @@ #endif MALLOC_UNBLOCK_INPUT; - eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); return val; } +/* Free BLOCK. This must be called to free memory allocated + with a call to lisp_malloc or lisp_align_malloc. */ + static void -lisp_align_free (void *block) +lisp_free (void *block) { - struct ablock *ablock = block; - struct ablocks *abase = ABLOCK_ABASE (ablock); - MALLOC_BLOCK_INPUT; + free (block); #if GC_MARK_STACK && !defined GC_MALLOC_CHECK mem_delete (mem_find (block)); #endif - /* Put on free list. */ - ablock->x.next_free = free_ablock; - free_ablock = ablock; - /* Update busy count. */ - ABLOCKS_BUSY (abase) - = (struct ablocks *) (-2 + (intptr_t) ABLOCKS_BUSY (abase)); - - if (2 > (intptr_t) ABLOCKS_BUSY (abase)) - { /* All the blocks are free. */ - int i = 0, aligned = (intptr_t) ABLOCKS_BUSY (abase); - struct ablock **tem = &free_ablock; - struct ablock *atop = &abase->blocks[aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1]; - - while (*tem) - { - if (*tem >= (struct ablock *) abase && *tem < atop) - { - i++; - *tem = (*tem)->x.next_free; - } - else - tem = &(*tem)->x.next_free; - } - eassert ((aligned & 1) == aligned); - eassert (i == (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1)); -#ifdef USE_POSIX_MEMALIGN - eassert ((uintptr_t) ABLOCKS_BASE (abase) % BLOCK_ALIGN == 0); -#endif - free (ABLOCKS_BASE (abase)); - } MALLOC_UNBLOCK_INPUT; } @@ -3757,7 +3622,7 @@ if (i == 0) free (spare_memory[i]); else if (i >= 1 && i <= 4) - lisp_align_free (spare_memory[i]); + lisp_free (spare_memory[i]); else lisp_free (spare_memory[i]); spare_memory[i] = 0; @@ -6292,7 +6157,7 @@ *cprev = cblk->next; /* Unhook from the free list. */ cons_free_list = cblk->conses[0].u.chain; - lisp_align_free (cblk); + lisp_free (cblk); } else { @@ -6338,7 +6203,7 @@ *fprev = fblk->next; /* Unhook from the free list. */ float_free_list = fblk->floats[0].u.chain; - lisp_align_free (fblk); + lisp_free (fblk); } else { @@ -6671,9 +6536,6 @@ pure_bytes_used_lisp = pure_bytes_used_non_lisp = 0; pure_bytes_used_before_overflow = 0; - /* Initialize the list of free aligned blocks. */ - free_ablock = NULL; - #if GC_MARK_STACK || defined GC_MALLOC_CHECK mem_init (); Vdead = make_pure_string ("DEAD", 4, 4, 0); ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-19 16:51 ` Dmitry Antipov @ 2012-06-19 17:13 ` Eli Zaretskii 2012-06-19 21:34 ` Stefan Monnier 2012-06-20 6:53 ` Paul Eggert 2 siblings, 0 replies; 25+ messages in thread From: Eli Zaretskii @ 2012-06-19 17:13 UTC (permalink / raw) To: Dmitry Antipov; +Cc: eggert, emacs-devel > Date: Tue, 19 Jun 2012 20:51:08 +0400 > From: Dmitry Antipov <dmantipov@yandex.ru> > Cc: Paul Eggert <eggert@cs.ucla.edu> > > this cleanup assumes that every malloc implementation > has reasonably efficient posix_memalign or memalign at least. Does the implementation of these in gmalloc.c satisfy the "reasonably efficient" requirement? If not, platforms that use gmalloc.c will be in trouble. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-19 16:51 ` Dmitry Antipov 2012-06-19 17:13 ` Eli Zaretskii @ 2012-06-19 21:34 ` Stefan Monnier 2012-06-20 6:47 ` Dmitry Antipov 2012-06-20 6:53 ` Paul Eggert 2 siblings, 1 reply; 25+ messages in thread From: Stefan Monnier @ 2012-06-19 21:34 UTC (permalink / raw) To: Dmitry Antipov; +Cc: Paul Eggert, emacs-devel > I believe this becomes more and more actual because more and more > of ancient systems with poor malloc implementations falls into their > graves; this cleanup assumes that every malloc implementation > has reasonably efficient posix_memalign or memalign at least. AFAIK, while posix_memalign might be useable in general, the old memalign is not because it can't be free'd (it can with glibc's memalign but not with all memaligns). > Does the implementation of these in gmalloc.c satisfy the "reasonably > efficient" requirement? If not, platforms that use gmalloc.c will be > in trouble. I remember looking into it, back then, but I can't remember what I fount out ;-) Stefan ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-19 21:34 ` Stefan Monnier @ 2012-06-20 6:47 ` Dmitry Antipov 2012-06-20 12:48 ` Stefan Monnier ` (2 more replies) 0 siblings, 3 replies; 25+ messages in thread From: Dmitry Antipov @ 2012-06-20 6:47 UTC (permalink / raw) To: Stefan Monnier, Paul Eggert, Eli Zaretskii; +Cc: Emacs development discussions [-- Attachment #1: Type: text/plain, Size: 422 bytes --] This code tries to utilize system malloc features and falls back to legacy aligned blocks management code if system malloc implementation is unknown, broken, or lacks aligned allocation routines. It will be great if someone can help to test this on Windows, OSX, old *BSD and other non-GNU/Linux systems; in particular, I believe OSX should have some specific aligned allocation stuff, which I'm not aware about. Dmitry [-- Attachment #2: lisp_align_malloc_cleanup.patch --] [-- Type: text/plain, Size: 13525 bytes --] === modified file 'configure.in' --- configure.in 2012-06-13 13:40:48 +0000 +++ configure.in 2012-06-20 05:59:43 +0000 @@ -2698,11 +2698,29 @@ __fpending strsignal setitimer \ sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \ gai_strerror mkstemp getline getdelim fsync sync \ -difftime posix_memalign \ +difftime memalign posix_memalign \ getpwent endpwent getgrent endgrent \ touchlock \ cfmakeraw cfsetspeed copysign __executable_start) +dnl If there is no posix_memalign, check whether pointer +dnl returned by memalign can be passed to free. +if test "$ac_cv_func_posix_memalign" != yes; then + if test "$ac_cv_func_memalign" = yes; then + AC_MSG_CHECKING([whether pointer returned by memalign can be passed to free]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <malloc.h> +main () { void *ptr = memalign (1024, 1024); +if (!ptr) return 1; free (ptr); return 0; } +]])], emacs_cv_working_memalign=yes, emacs_cv_working_memalign=no, + emacs_cv_working_memalign=no) + AC_MSG_RESULT($emacs_cv_working_memalign) + if test $emacs_cv_working_memalign = yes; then + AC_DEFINE(HAVE_WORKING_MEMALIGN, 1, + [Define to 1 if pointer returned by `memalign' can be passed to `free'.]) + fi + fi +fi + dnl Cannot use AC_CHECK_FUNCS AC_CACHE_CHECK([for __builtin_unwind_init], emacs_cv_func___builtin_unwind_init, === modified file 'src/alloc.c' --- src/alloc.c 2012-06-19 16:56:28 +0000 +++ src/alloc.c 2012-06-20 06:35:05 +0000 @@ -309,9 +309,6 @@ MEM_TYPE_VECTOR_BLOCK }; -static void *lisp_malloc (size_t, enum mem_type); - - #if GC_MARK_STACK || defined GC_MALLOC_CHECK #if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES @@ -888,19 +885,37 @@ return Qnil; } +#if ! USE_LSB_TAG + +/* Used to catch invalid address when debugging. */ + +void *lisp_malloc_loser EXTERNALLY_VISIBLE; + +/* Nonzero if the memory at ADDR can be + addressed thru a Lisp object's pointer. */ + +static inline void +verify_address (char *addr) +{ + Lisp_Object obj; + + XSETCONS (obj, addr); + if ((char *) XCONS (obj) == addr) + return 1; + lisp_malloc_loser = addr; + return 0; +} + +#endif /* not USE_LSB_TAG */ /* Like malloc but used for allocating Lisp data. NBYTES is the number of bytes to allocate, TYPE describes the intended use of the allocated memory block (for strings, for conses, ...). */ -#if ! USE_LSB_TAG -void *lisp_malloc_loser EXTERNALLY_VISIBLE; -#endif - static void * lisp_malloc (size_t nbytes, enum mem_type type) { - register void *val; + void *val; MALLOC_BLOCK_INPUT; @@ -908,24 +923,33 @@ allocated_mem_type = type; #endif +#ifdef DOUG_LEA_MALLOC + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); +#endif val = (void *) malloc (nbytes); +#ifdef DOUG_LEA_MALLOC + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); +#endif + + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } #if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, - that's equivalent to running out of memory. */ - if (val && type != MEM_TYPE_NON_LISP) + if (val && type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) { - Lisp_Object tem; - XSETCONS (tem, (char *) val + nbytes - 1); - if ((char *) XCONS (tem) != (char *) val + nbytes - 1) - { - lisp_malloc_loser = val; - free (val); - val = 0; - } + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); } -#endif +#endif /* not USE_LSB_TAG */ #if GC_MARK_STACK && !defined GC_MALLOC_CHECK if (val && type != MEM_TYPE_NON_LISP) @@ -933,13 +957,11 @@ #endif MALLOC_UNBLOCK_INPUT; - if (!val && nbytes) - memory_full (nbytes); return val; } -/* Free BLOCK. This must be called to free memory allocated with a - call to lisp_malloc. */ +/* Free BLOCK. This must be called to free + memory allocated with a call to lisp_malloc. */ static void lisp_free (void *block) @@ -952,30 +974,31 @@ MALLOC_UNBLOCK_INPUT; } -/***** Allocation of aligned blocks of memory to store Lisp data. *****/ - -/* The entry point is lisp_align_malloc which returns blocks of at most - BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */ - -#if defined (HAVE_POSIX_MEMALIGN) && defined (SYSTEM_MALLOC) -#define USE_POSIX_MEMALIGN 1 -#endif +/* Allocation of aligned blocks is somewhat tricky. On POSIX systems, + HAVE_POSIX_MEMALIGN is defined, so we can use posix_memalign and free. + Some systems lacks posix_memalign, but provides memalign; for such a + systems, we're checking whether pointer returned by memalign can be + passed to free. If this is so, we define HAVE_WORKING_MEMALIGN and + use memalign and free. For native Windows build with MSVC, we use + _aligned_malloc and _aligned_free. If none of the above, we use + internal_align_alloc and internal_align_free. */ /* BLOCK_ALIGN has to be a power of 2. */ + #define BLOCK_ALIGN (1 << 10) -/* Padding to leave at the end of a malloc'd block. This is to give - malloc a chance to minimize the amount of memory wasted to alignment. - It should be tuned to the particular malloc library used. - On glibc-2.3.2, malloc never tries to align, so a padding of 0 is best. - posix_memalign on the other hand would ideally prefer a value of 4 - because otherwise, there's 1020 bytes wasted between each ablocks. - In Emacs, testing shows that those 1020 can most of the time be - efficiently used by malloc to place other objects, so a value of 0 can - still preferable unless you have a lot of aligned blocks and virtually - nothing else. */ +#if ! HAVE_POSIX_MEMALIGN && ! HAVE_WORKING_MEMALIGN && ! _MSC_VER + +/* Here we assume that malloc implementation has + nothing about aligned blocks management. */ + +/* Padding to leave at the end of a malloc'd block. */ + #define BLOCK_PADDING 0 -#define BLOCK_BYTES \ + +/* Maximum amount of memory in aligned block. */ + +#define BLOCK_BYTES \ (BLOCK_ALIGN - sizeof (struct ablocks *) - BLOCK_PADDING) /* Internal data structures and constants. */ @@ -983,6 +1006,7 @@ #define ABLOCKS_SIZE 16 /* An aligned block of memory. */ + struct ablock { union @@ -1008,12 +1032,14 @@ }; /* A bunch of consecutive aligned blocks. */ + struct ablocks { struct ablock blocks[ABLOCKS_SIZE]; }; -/* Size of the block requested from malloc or posix_memalign. */ +/* Size of the block requested from underlying malloc. */ + #define ABLOCKS_BYTES (sizeof (struct ablocks) - BLOCK_PADDING) #define ABLOCK_ABASE(block) \ @@ -1022,94 +1048,43 @@ : (block)->abase) /* Virtual `busy' field. */ + #define ABLOCKS_BUSY(abase) ((abase)->blocks[0].abase) /* Pointer to the (not necessarily aligned) malloc block. */ -#ifdef USE_POSIX_MEMALIGN -#define ABLOCKS_BASE(abase) (abase) -#else -#define ABLOCKS_BASE(abase) \ + +#define ABLOCKS_BASE(abase) \ (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void**)abase)[-1]) -#endif /* The list of free ablock. */ + static struct ablock *free_ablock; -/* Allocate an aligned block of nbytes. - Alignment is on a multiple of BLOCK_ALIGN and `nbytes' has to be - smaller or equal to BLOCK_BYTES. */ +/* Allocate an aligned block of NBYTES. */ + static void * -lisp_align_malloc (size_t nbytes, enum mem_type type) +internal_align_alloc (size_t nbytes) { void *base, *val; struct ablocks *abase; eassert (nbytes <= BLOCK_BYTES); - MALLOC_BLOCK_INPUT; - -#ifdef GC_MALLOC_CHECK - allocated_mem_type = type; -#endif - if (!free_ablock) { int i; intptr_t aligned; /* int gets warning casting to 64-bit pointer. */ -#ifdef DOUG_LEA_MALLOC - /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed - because mapped region contents are not preserved in - a dumped Emacs. */ - mallopt (M_MMAP_MAX, 0); -#endif - -#ifdef USE_POSIX_MEMALIGN - { - int err = posix_memalign (&base, BLOCK_ALIGN, ABLOCKS_BYTES); - if (err) - base = NULL; - abase = base; - } -#else base = malloc (ABLOCKS_BYTES); abase = ALIGN (base, BLOCK_ALIGN); -#endif - if (base == 0) - { - MALLOC_UNBLOCK_INPUT; - memory_full (ABLOCKS_BYTES); - } + if (base == NULL) + return base; aligned = (base == abase); if (!aligned) ((void**)abase)[-1] = base; -#ifdef DOUG_LEA_MALLOC - /* Back to a reasonable maximum of mmap'ed areas. */ - mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); -#endif - -#if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, that's equivalent to - running out of memory. */ - if (type != MEM_TYPE_NON_LISP) - { - Lisp_Object tem; - char *end = (char *) base + ABLOCKS_BYTES - 1; - XSETCONS (tem, end); - if ((char *) XCONS (tem) != end) - { - lisp_malloc_loser = base; - free (base); - MALLOC_UNBLOCK_INPUT; - memory_full (SIZE_MAX); - } - } -#endif - /* Initialize the blocks and put them on the free list. If `base' was not properly aligned, we can't use the last block. */ for (i = 0; i < (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1); i++) @@ -1133,27 +1108,15 @@ val = free_ablock; free_ablock = free_ablock->x.next_free; -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK - if (type != MEM_TYPE_NON_LISP) - mem_insert (val, (char *) val + nbytes, type); -#endif - - MALLOC_UNBLOCK_INPUT; - - eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); return val; } static void -lisp_align_free (void *block) +internal_align_free (void *block) { struct ablock *ablock = block; struct ablocks *abase = ABLOCK_ABASE (ablock); - MALLOC_BLOCK_INPUT; -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK - mem_delete (mem_find (block)); -#endif /* Put on free list. */ ablock->x.next_free = free_ablock; free_ablock = ablock; @@ -1179,11 +1142,110 @@ } eassert ((aligned & 1) == aligned); eassert (i == (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1)); -#ifdef USE_POSIX_MEMALIGN - eassert ((uintptr_t) ABLOCKS_BASE (abase) % BLOCK_ALIGN == 0); -#endif free (ABLOCKS_BASE (abase)); } +} + +#else /* ! HAVE_POSIX_MEMALIGN && ! HAVE_WORKING_MEMALIGN && ! _MSC_VER */ + +/* Here we assume malloc implementation with posix_memalign, + or malloc implementation with working memalign, or MSVC. */ + +/* Padding to leave at the end of a malloc'd block. This should help + the malloc implementation to allocate aligned blocks consecutively. + FIXME: tuned to fit glibc malloc, may be suboptimal for others. */ + +#define BLOCK_PADDING sizeof (long) + +/* Maximum amount of memory in aligned block. */ + +#define BLOCK_BYTES (BLOCK_ALIGN - BLOCK_PADDING) + +#endif /* ! HAVE_POSIX_MEMALIGN && ! HAVE_WORKING_MEMALIGN && ! _MSC_VER */ + +/* Like lisp_malloc, but allocates aligned block of at + most BLOCK_BYTES aligned on a BLOCK_ALIGN boundary. */ + +static void * +lisp_align_malloc (size_t nbytes, enum mem_type type) +{ + void *val; + + eassert (nbytes <= BLOCK_BYTES); + + MALLOC_BLOCK_INPUT; + +#ifdef GC_MALLOC_CHECK + allocated_mem_type = type; +#endif + +#ifdef DOUG_LEA_MALLOC + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); +#endif + +#ifdef HAVE_POSIX_MEMALIGN + if (posix_memalign (&val, BLOCK_ALIGN, nbytes)) + val = NULL; +#elif HAVE_WORKING_MEMALIGN + val = memalign (BLOCK_ALIGN, nbytes); +#elif _MSC_VER + /* Yes, the order of arguments is correct. */ + val = _aligned_malloc (nbytes, BLOCK_ALIGN); +#else + val = internal_align_alloc (nbytes); +#endif + +#ifdef DOUG_LEA_MALLOC + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); +#endif + + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } + +#if ! USE_LSB_TAG + if (type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) + { + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); + } +#endif /* not USE_LSB_TAG */ + +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK + if (type != MEM_TYPE_NON_LISP) + mem_insert (val, (char *) val + nbytes, type); +#endif + + MALLOC_UNBLOCK_INPUT; + eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); + return val; +} + +/* Free aligned BLOCK. This must be called to free + memory allocated with a call to lisp_align_malloc. */ + +static void +lisp_align_free (void *block) +{ + MALLOC_BLOCK_INPUT; +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK + mem_delete (mem_find (block)); +#endif +#if HAVE_POSIX_MEMALIGN || HAVE_WORKING_MEMALIGN + free (block); +#elif _MSC_VER + _aligned_free (block); +#else + internal_align_free (block); +#endif MALLOC_UNBLOCK_INPUT; } @@ -6671,8 +6733,10 @@ pure_bytes_used_lisp = pure_bytes_used_non_lisp = 0; pure_bytes_used_before_overflow = 0; +#if ! HAVE_POSIX_MEMALIGN && ! HAVE_WORKING_MEMALIGN && ! _MSC_VER /* Initialize the list of free aligned blocks. */ free_ablock = NULL; +#endif #if GC_MARK_STACK || defined GC_MALLOC_CHECK mem_init (); ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 6:47 ` Dmitry Antipov @ 2012-06-20 12:48 ` Stefan Monnier 2012-06-20 13:54 ` Dmitry Antipov 2012-06-20 17:06 ` Eli Zaretskii 2012-06-20 17:59 ` Wolfgang Jenkner 2 siblings, 1 reply; 25+ messages in thread From: Stefan Monnier @ 2012-06-20 12:48 UTC (permalink / raw) To: Dmitry Antipov; +Cc: Eli Zaretskii, Paul Eggert, Emacs development discussions > It will be great if someone can help to test this on Windows, OSX, old *BSD > and other non-GNU/Linux systems; in particular, I believe OSX should have > some specific aligned allocation stuff, which I'm not aware about. I'm not completely sure the test is sufficiently thorough to catch all problems, and more importantly it doesn't test the efficiency of memalign. Stefan ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 12:48 ` Stefan Monnier @ 2012-06-20 13:54 ` Dmitry Antipov 2012-06-20 15:41 ` Stefan Monnier 2012-06-20 17:10 ` Eli Zaretskii 0 siblings, 2 replies; 25+ messages in thread From: Dmitry Antipov @ 2012-06-20 13:54 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, Paul Eggert, Emacs development discussions [-- Attachment #1: Type: text/plain, Size: 502 bytes --] On 06/20/2012 04:48 PM, Stefan Monnier wrote: > I'm not completely sure the test is sufficiently thorough to catch all > problems, and more importantly it doesn't test the efficiency > of memalign. If it's just a matter of configure test, different methods may be used. For example, attached code checks whether memalign tends to create holes while allocating (1024 - 8) blocks aligned at 1024-bytes boundary; optimal pad (BLOCK_PADDING in current code) may be detected with the similar way. Dmitry [-- Attachment #2: test-memalign.c --] [-- Type: text/plain, Size: 1122 bytes --] /* Simple check whether memalign tends to waste some space. */ #include <stdio.h> #include <stdlib.h> #include <malloc.h> int main (int argc, char *argv[]) { int i, j, bad = 0; const size_t blksz = 1024, padsz = 8; void *p, *prev; /* make some chaos in the heap */ for (i = 0; i <= 1000; i++) for (j = 8; j <= 1024; j += 8) { p = malloc (j); if (((i + j) & 7) == 0) free (p); } /* test asks for BLKSZ - PADSZ bytes aligned at BLKSZ boundary */ for (i = 0, prev = NULL; i < 16; i++) { p = memalign (blksz, blksz - padsz); if (!p) bad++; else if (prev && p - prev > blksz) bad++; prev = p; } /* make even more chaos with larger chunks */ for (i = 0; i <= 1000; i++) for (j = 8; j <= 1024; j += 8) { p = malloc (j * 16); if (((i + j) & 15) == 0) free (p); } /* test again */ for (i = 0, prev = NULL; i < 16; i++) { p = memalign (blksz, blksz - padsz); if (!p) bad++; else if (prev && p - prev > blksz) bad++; prev = p; } /* zero means memalign looks good enough */ return bad; } ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 13:54 ` Dmitry Antipov @ 2012-06-20 15:41 ` Stefan Monnier 2012-06-20 17:10 ` Eli Zaretskii 1 sibling, 0 replies; 25+ messages in thread From: Stefan Monnier @ 2012-06-20 15:41 UTC (permalink / raw) To: Dmitry Antipov; +Cc: Eli Zaretskii, Paul Eggert, Emacs development discussions >> I'm not completely sure the test is sufficiently thorough to catch all >> problems, and more importantly it doesn't test the efficiency >> of memalign. > If it's just a matter of configure test, different methods may be used. > For example, attached code checks whether memalign tends to create holes > while allocating (1024 - 8) blocks aligned at 1024-bytes boundary; You want to mix those allocations with a few others of different sizes, since some of the problems show up in those cases (e.g. "memalign(1024); malloc(10); memalign(1024)" might end up wasting 1KB between the malloc and the second memalign). > optimal pad (BLOCK_PADDING in current code) may be detected with the > similar way. That would be good. Stefan ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 13:54 ` Dmitry Antipov 2012-06-20 15:41 ` Stefan Monnier @ 2012-06-20 17:10 ` Eli Zaretskii 2012-06-21 4:27 ` Dmitry Antipov 1 sibling, 1 reply; 25+ messages in thread From: Eli Zaretskii @ 2012-06-20 17:10 UTC (permalink / raw) To: Dmitry Antipov; +Cc: eggert, monnier, emacs-devel > Date: Wed, 20 Jun 2012 17:54:38 +0400 > From: Dmitry Antipov <dmantipov@yandex.ru> > CC: Paul Eggert <eggert@cs.ucla.edu>, Eli Zaretskii <eliz@gnu.org>, > Emacs development discussions <emacs-devel@gnu.org> > > For example, attached code checks whether memalign tends to create holes > while allocating (1024 - 8) blocks aligned at 1024-bytes boundary; optimal > pad (BLOCK_PADDING in current code) may be detected with the similar way. Is it possible to incorporate such tests into Emacs, and write some Lisp to execute the tests and report the results? I'm asking because it's hard to use this test as-is on MS-Windows, since all of the related functions used by the Windows build are reimplemented by Emacs sources, they are not in any system library, and so linking a stand-alone test program is not easy. TIA P.S. If memory fragmentation is what you worry about, then doesn't ralloc.c free us from this, on platforms that use it? ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 17:10 ` Eli Zaretskii @ 2012-06-21 4:27 ` Dmitry Antipov 2012-06-21 16:29 ` Eli Zaretskii 2012-06-21 21:34 ` Aligned blocks management: obsolete? Richard Stallman 0 siblings, 2 replies; 25+ messages in thread From: Dmitry Antipov @ 2012-06-21 4:27 UTC (permalink / raw) To: Eli Zaretskii; +Cc: eggert, monnier, emacs-devel [-- Attachment #1: Type: text/plain, Size: 1721 bytes --] On 06/20/2012 09:10 PM, Eli Zaretskii wrote: >> From: Dmitry Antipov <dmantipov@yandex.ru> >> CC: Paul Eggert <eggert@cs.ucla.edu>, Eli Zaretskii <eliz@gnu.org>, >> Emacs development discussions <emacs-devel@gnu.org> >> >> For example, attached code checks whether memalign tends to create holes >> while allocating (1024 - 8) blocks aligned at 1024-bytes boundary; optimal >> pad (BLOCK_PADDING in current code) may be detected with the similar way. > > Is it possible to incorporate such tests into Emacs, and write some > Lisp to execute the tests and report the results? Such a test can be embedded into configure script. Moreover, I believe it might be applicable outside of Emacs; IMHO, it would be nice to have such a test as a part of gnulib. > I'm asking because it's hard to use this test as-is on MS-Windows, > since all of the related functions used by the Windows build are > reimplemented by Emacs sources, they are not in any system library, > and so linking a stand-alone test program is not easy. Previous discussions shows my incompetence in Windows-related areas of Emacs development, so I have no ideas. > P.S. If memory fragmentation is what you worry about, then doesn't > ralloc.c free us from this, on platforms that use it? I don't know how ralloc.c works. In general, I believe moving/compacting GC is the only way to reduce fragmentation; since current design makes it impossible to move/compact Lisp objects, any underlying allocator can't really help to reduce fragmentation. As for the lisp_align_{malloc,free}, this cleanup was designed just because I have some experimental mmap stuff and want to make some simplifications before introducing even more complexities :-). Dmitry [-- Attachment #2: lisp_align_malloc_cleanup.patch --] [-- Type: text/plain, Size: 15812 bytes --] === modified file 'configure.in' --- configure.in 2012-06-13 13:40:48 +0000 +++ configure.in 2012-06-21 04:25:01 +0000 @@ -2698,11 +2698,125 @@ __fpending strsignal setitimer \ sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \ gai_strerror mkstemp getline getdelim fsync sync \ -difftime posix_memalign \ +difftime memalign posix_memalign \ getpwent endpwent getgrent endgrent \ touchlock \ cfmakeraw cfsetspeed copysign __executable_start) +dnl Check whether posix_memalign can allocate blocks consecutively +if test "$ac_cv_func_posix_memalign" = yes; then + AC_MSG_CHECKING([for block padding size to use with posix_memalign]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdlib.h> +int +main () +{ + int i, j, bad; + void *p, *prev; + size_t padsz, blksz = 1024; + + for (padsz = 0; padsz <= 64; padsz += sizeof (long)) + { + bad = 0; + /* make some chaos in the heap */ + for (i = 0; i <= 1000; i++) + for (j = 8; j <= 1024; j += 8) + { + p = malloc (j); + if (!p) + return 255; + if (((i + j) & 7) == 0) + free (p); + } + + /* test asks for BLKSZ - PADSZ bytes aligned at BLKSZ boundary */ + for (i = 0, prev = NULL; i < 16; i++) + { + if (posix_memalign (&p, blksz, blksz - padsz)) + bad++; + else if (prev && p - prev > blksz) + bad++; + prev = p; + } + + /* zero means posix_memalign looks good enough with this PADSZ */ + if (!bad) + return padsz; + } + + /* no suitable PADSZ was found, posix_memalign looks poor */ + return 255; +}]])], emacs_cv_posix_memalign_pad=255, emacs_cv_posix_memalign_pad=$?, + emacs_cv_posix_memalign_pad=255) + if test $emacs_cv_posix_memalign_pad -le 64; then + AC_MSG_RESULT($emacs_cv_posix_memalign_pad) + AC_DEFINE(POSIX_MEMALIGN_WORKS, 1, + [Define to 1 if you have good enough `posix_memalign' function.]) + AC_DEFINE_UNQUOTED(BLOCK_PADDING, $emacs_cv_posix_memalign_pad, + [Block padding used to allocate aligned blocks.]) + else + AC_MSG_RESULT([not detected, posix_memalign will not be used]) + fi +fi + +dnl If posix_memalign isn't available or tends to create holes +dnl between blocks, check whether memalign performs better +if test "$emacs_cv_posix_memalign_pad" -gt 64; then + if test "$ac_cv_func_memalign" = yes; then + AC_MSG_CHECKING([for block padding size for memalign]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <malloc.h> +int +main () +{ + int i, j, bad; + void *p, *prev; + size_t padsz, blksz = 1024; + + for (padsz = 0; padsz <= 64; padsz += sizeof (long)) + { + bad = 0; + /* make some chaos in the heap */ + for (i = 0; i <= 1000; i++) + for (j = 8; j <= 1024; j += 8) + { + p = malloc (j); + if (!p) + return 255; + if (((i + j) & 7) == 0) + free (p); + } + + /* test asks for BLKSZ - PADSZ bytes aligned at BLKSZ boundary */ + for (i = 0, prev = NULL; i < 16; i++) + { + p = memalign (blksz, blksz - padsz); + if (!p) + bad++; + else if (prev && p - prev > blksz) + bad++; + prev = p; + } + + /* zero means memalign looks good enough with this PADSZ */ + if (!bad) + return padsz; + } + + /* no suitable PADSZ was found, memalign looks poor */ + return 255; +}]])], emacs_cv_memalign_pad=255, emacs_cv_memalign_pad=$?, + emacs_cv_memalign_pad=255) + if test $emacs_cv_memalign_pad -le 64; then + AC_MSG_RESULT($emacs_cv_memalign_pad) + AC_DEFINE(MEMALIGN_WORKS, 1, + [Define to 1 if you have good enough `memalign' function.]) + AC_DEFINE_UNQUOTED(BLOCK_PADDING, $emacs_cv_memalign_pad, + [Block padding used to allocate aligned blocks.]) + else + AC_MSG_RESULT([not detected, memalign will not be used]) + fi + fi +fi + dnl Cannot use AC_CHECK_FUNCS AC_CACHE_CHECK([for __builtin_unwind_init], emacs_cv_func___builtin_unwind_init, === modified file 'src/alloc.c' --- src/alloc.c 2012-06-19 16:56:28 +0000 +++ src/alloc.c 2012-06-21 04:24:33 +0000 @@ -309,9 +309,6 @@ MEM_TYPE_VECTOR_BLOCK }; -static void *lisp_malloc (size_t, enum mem_type); - - #if GC_MARK_STACK || defined GC_MALLOC_CHECK #if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES @@ -888,19 +885,37 @@ return Qnil; } +#if ! USE_LSB_TAG + +/* Used to catch invalid address when debugging. */ + +void *lisp_malloc_loser EXTERNALLY_VISIBLE; + +/* Nonzero if the memory at ADDR can be + addressed thru a Lisp object's pointer. */ + +static inline int +verify_address (char *addr) +{ + Lisp_Object obj; + + XSETCONS (obj, addr); + if ((char *) XCONS (obj) == addr) + return 1; + lisp_malloc_loser = addr; + return 0; +} + +#endif /* not USE_LSB_TAG */ /* Like malloc but used for allocating Lisp data. NBYTES is the number of bytes to allocate, TYPE describes the intended use of the allocated memory block (for strings, for conses, ...). */ -#if ! USE_LSB_TAG -void *lisp_malloc_loser EXTERNALLY_VISIBLE; -#endif - static void * lisp_malloc (size_t nbytes, enum mem_type type) { - register void *val; + void *val; MALLOC_BLOCK_INPUT; @@ -908,24 +923,33 @@ allocated_mem_type = type; #endif +#ifdef DOUG_LEA_MALLOC + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); +#endif val = (void *) malloc (nbytes); +#ifdef DOUG_LEA_MALLOC + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); +#endif + + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } #if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, - that's equivalent to running out of memory. */ - if (val && type != MEM_TYPE_NON_LISP) + if (val && type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) { - Lisp_Object tem; - XSETCONS (tem, (char *) val + nbytes - 1); - if ((char *) XCONS (tem) != (char *) val + nbytes - 1) - { - lisp_malloc_loser = val; - free (val); - val = 0; - } + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); } -#endif +#endif /* not USE_LSB_TAG */ #if GC_MARK_STACK && !defined GC_MALLOC_CHECK if (val && type != MEM_TYPE_NON_LISP) @@ -933,13 +957,11 @@ #endif MALLOC_UNBLOCK_INPUT; - if (!val && nbytes) - memory_full (nbytes); return val; } -/* Free BLOCK. This must be called to free memory allocated with a - call to lisp_malloc. */ +/* Free BLOCK. This must be called to free + memory allocated with a call to lisp_malloc. */ static void lisp_free (void *block) @@ -952,30 +974,33 @@ MALLOC_UNBLOCK_INPUT; } -/***** Allocation of aligned blocks of memory to store Lisp data. *****/ - -/* The entry point is lisp_align_malloc which returns blocks of at most - BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */ - -#if defined (HAVE_POSIX_MEMALIGN) && defined (SYSTEM_MALLOC) -#define USE_POSIX_MEMALIGN 1 -#endif +/* Allocation of aligned blocks is somewhat tricky. If posix_memalign is + available, configure tries to determine the block padding value to help + posix_memalign allocate blocks of (1024 - padding) bytes without holes + between them. If suitable padding is found, we define POSIX_MEMALIGN_WORKS, + BLOCK_PADDING to padding value and use posix_memalign and free. Some + systems lacks posix_memalign, but provides memalign; for such a system, + configure performs similar check for memalign. If suitable padding is + found, we define MEMALIGN_WORKS, BLOCK_PADDING to padding value and use + memalign and free. If none of the above, we use internal_align_alloc and + internal_align_free. */ /* BLOCK_ALIGN has to be a power of 2. */ + #define BLOCK_ALIGN (1 << 10) -/* Padding to leave at the end of a malloc'd block. This is to give - malloc a chance to minimize the amount of memory wasted to alignment. - It should be tuned to the particular malloc library used. - On glibc-2.3.2, malloc never tries to align, so a padding of 0 is best. - posix_memalign on the other hand would ideally prefer a value of 4 - because otherwise, there's 1020 bytes wasted between each ablocks. - In Emacs, testing shows that those 1020 can most of the time be - efficiently used by malloc to place other objects, so a value of 0 can - still preferable unless you have a lot of aligned blocks and virtually - nothing else. */ +#if ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS + +/* Here we assume that malloc implementation has + nothing about aligned blocks management. */ + +/* Padding to leave at the end of a malloc'd block. */ + #define BLOCK_PADDING 0 -#define BLOCK_BYTES \ + +/* Maximum amount of memory in aligned block. */ + +#define BLOCK_BYTES \ (BLOCK_ALIGN - sizeof (struct ablocks *) - BLOCK_PADDING) /* Internal data structures and constants. */ @@ -983,6 +1008,7 @@ #define ABLOCKS_SIZE 16 /* An aligned block of memory. */ + struct ablock { union @@ -1008,12 +1034,14 @@ }; /* A bunch of consecutive aligned blocks. */ + struct ablocks { struct ablock blocks[ABLOCKS_SIZE]; }; -/* Size of the block requested from malloc or posix_memalign. */ +/* Size of the block requested from underlying malloc. */ + #define ABLOCKS_BYTES (sizeof (struct ablocks) - BLOCK_PADDING) #define ABLOCK_ABASE(block) \ @@ -1022,94 +1050,43 @@ : (block)->abase) /* Virtual `busy' field. */ + #define ABLOCKS_BUSY(abase) ((abase)->blocks[0].abase) /* Pointer to the (not necessarily aligned) malloc block. */ -#ifdef USE_POSIX_MEMALIGN -#define ABLOCKS_BASE(abase) (abase) -#else -#define ABLOCKS_BASE(abase) \ + +#define ABLOCKS_BASE(abase) \ (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void**)abase)[-1]) -#endif /* The list of free ablock. */ + static struct ablock *free_ablock; -/* Allocate an aligned block of nbytes. - Alignment is on a multiple of BLOCK_ALIGN and `nbytes' has to be - smaller or equal to BLOCK_BYTES. */ +/* Allocate an aligned block of NBYTES. */ + static void * -lisp_align_malloc (size_t nbytes, enum mem_type type) +internal_align_alloc (size_t nbytes) { void *base, *val; struct ablocks *abase; eassert (nbytes <= BLOCK_BYTES); - MALLOC_BLOCK_INPUT; - -#ifdef GC_MALLOC_CHECK - allocated_mem_type = type; -#endif - if (!free_ablock) { int i; intptr_t aligned; /* int gets warning casting to 64-bit pointer. */ -#ifdef DOUG_LEA_MALLOC - /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed - because mapped region contents are not preserved in - a dumped Emacs. */ - mallopt (M_MMAP_MAX, 0); -#endif - -#ifdef USE_POSIX_MEMALIGN - { - int err = posix_memalign (&base, BLOCK_ALIGN, ABLOCKS_BYTES); - if (err) - base = NULL; - abase = base; - } -#else base = malloc (ABLOCKS_BYTES); abase = ALIGN (base, BLOCK_ALIGN); -#endif - if (base == 0) - { - MALLOC_UNBLOCK_INPUT; - memory_full (ABLOCKS_BYTES); - } + if (base == NULL) + return base; aligned = (base == abase); if (!aligned) ((void**)abase)[-1] = base; -#ifdef DOUG_LEA_MALLOC - /* Back to a reasonable maximum of mmap'ed areas. */ - mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); -#endif - -#if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, that's equivalent to - running out of memory. */ - if (type != MEM_TYPE_NON_LISP) - { - Lisp_Object tem; - char *end = (char *) base + ABLOCKS_BYTES - 1; - XSETCONS (tem, end); - if ((char *) XCONS (tem) != end) - { - lisp_malloc_loser = base; - free (base); - MALLOC_UNBLOCK_INPUT; - memory_full (SIZE_MAX); - } - } -#endif - /* Initialize the blocks and put them on the free list. If `base' was not properly aligned, we can't use the last block. */ for (i = 0; i < (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1); i++) @@ -1133,27 +1110,15 @@ val = free_ablock; free_ablock = free_ablock->x.next_free; -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK - if (type != MEM_TYPE_NON_LISP) - mem_insert (val, (char *) val + nbytes, type); -#endif - - MALLOC_UNBLOCK_INPUT; - - eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); return val; } static void -lisp_align_free (void *block) +internal_align_free (void *block) { struct ablock *ablock = block; struct ablocks *abase = ABLOCK_ABASE (ablock); - MALLOC_BLOCK_INPUT; -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK - mem_delete (mem_find (block)); -#endif /* Put on free list. */ ablock->x.next_free = free_ablock; free_ablock = ablock; @@ -1179,11 +1144,103 @@ } eassert ((aligned & 1) == aligned); eassert (i == (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1)); -#ifdef USE_POSIX_MEMALIGN - eassert ((uintptr_t) ABLOCKS_BASE (abase) % BLOCK_ALIGN == 0); -#endif free (ABLOCKS_BASE (abase)); } +} + +#else /* ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS */ + +/* Here we assume that malloc implementation has either posix_memalign + or memalign, and suitable BLOCK_PADDING value was detected by configure. */ + +#ifndef BLOCK_PADDING +#error "unknown BLOCK_PADDING" +#endif + +/* Maximum amount of memory in aligned block. */ + +#define BLOCK_BYTES (BLOCK_ALIGN - BLOCK_PADDING) + +#endif /* ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS */ + +/* Like lisp_malloc, but allocates aligned block of at + most BLOCK_BYTES aligned on a BLOCK_ALIGN boundary. */ + +static void * +lisp_align_malloc (size_t nbytes, enum mem_type type) +{ + void *val; + + eassert (nbytes <= BLOCK_BYTES); + + MALLOC_BLOCK_INPUT; + +#ifdef GC_MALLOC_CHECK + allocated_mem_type = type; +#endif + +#ifdef DOUG_LEA_MALLOC + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); +#endif + +#ifdef POSIX_MEMALIGN_WORKS + if (posix_memalign (&val, BLOCK_ALIGN, nbytes)) + val = NULL; +#elif MEMALIGN_WORKS + val = memalign (BLOCK_ALIGN, nbytes); +#else + val = internal_align_alloc (nbytes); +#endif + +#ifdef DOUG_LEA_MALLOC + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); +#endif + + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } + +#if ! USE_LSB_TAG + if (type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) + { + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); + } +#endif /* not USE_LSB_TAG */ + +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK + if (type != MEM_TYPE_NON_LISP) + mem_insert (val, (char *) val + nbytes, type); +#endif + + MALLOC_UNBLOCK_INPUT; + eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); + return val; +} + +/* Free aligned BLOCK. This must be called to free + memory allocated with a call to lisp_align_malloc. */ + +static void +lisp_align_free (void *block) +{ + MALLOC_BLOCK_INPUT; +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK + mem_delete (mem_find (block)); +#endif +#if POSIX_MEMALIGN_WORKS || MEMALIGN_WORKS + free (block); +#else + internal_align_free (block); +#endif MALLOC_UNBLOCK_INPUT; } @@ -6671,8 +6728,10 @@ pure_bytes_used_lisp = pure_bytes_used_non_lisp = 0; pure_bytes_used_before_overflow = 0; +#if ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS /* Initialize the list of free aligned blocks. */ free_ablock = NULL; +#endif #if GC_MARK_STACK || defined GC_MALLOC_CHECK mem_init (); ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-21 4:27 ` Dmitry Antipov @ 2012-06-21 16:29 ` Eli Zaretskii 2012-07-04 8:39 ` Old topic(s) again [was: Re: Aligned blocks management: obsolete?] Dmitry Antipov 2012-06-21 21:34 ` Aligned blocks management: obsolete? Richard Stallman 1 sibling, 1 reply; 25+ messages in thread From: Eli Zaretskii @ 2012-06-21 16:29 UTC (permalink / raw) To: Dmitry Antipov; +Cc: eggert, monnier, emacs-devel > Date: Thu, 21 Jun 2012 08:27:14 +0400 > From: Dmitry Antipov <dmantipov@yandex.ru> > CC: monnier@iro.umontreal.ca, eggert@cs.ucla.edu, emacs-devel@gnu.org > > > P.S. If memory fragmentation is what you worry about, then doesn't > > ralloc.c free us from this, on platforms that use it? > > I don't know how ralloc.c works. It hooks into sbrk, and when malloc asks for more memory by calling sbrk, ralloc.c relocates buffer text trying to free a bug enough chunk, before it lets the call to sbrk to proceed. > In general, I believe moving/compacting GC is the only way to reduce > fragmentation; since current design makes it impossible to > move/compact Lisp objects, any underlying allocator can't really > help to reduce fragmentation. Maybe I don't understand what you mean by move/compact, but aren't buffer text and data of a Lisp string already designed to support such moving/compacting? The pointer to the text is obtained by a second dereference, so the Lisp object representing buffer and string stays unmodified when the text is relocated. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Old topic(s) again [was: Re: Aligned blocks management: obsolete?] 2012-06-21 16:29 ` Eli Zaretskii @ 2012-07-04 8:39 ` Dmitry Antipov 2012-07-04 13:11 ` Stefan Monnier 0 siblings, 1 reply; 25+ messages in thread From: Dmitry Antipov @ 2012-07-04 8:39 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 18 bytes --] Likewise. Dmitry [-- Attachment #2: lisp_alloc.patch --] [-- Type: text/plain, Size: 15523 bytes --] === modified file 'configure.in' --- configure.in 2012-07-04 08:07:26 +0000 +++ configure.in 2012-07-04 08:28:33 +0000 @@ -2704,12 +2704,106 @@ __fpending strsignal setitimer \ sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \ gai_strerror mkstemp getline getdelim fsync sync \ -difftime posix_memalign \ +difftime memalign posix_memalign \ getpwent endpwent getgrent endgrent \ touchlock \ strcasecmp strncasecmp \ cfmakeraw cfsetspeed copysign __executable_start) +dnl Check whether posix_memalign can allocate blocks consecutively. +if test "$ac_cv_func_posix_memalign" = yes; then + AC_MSG_CHECKING([for block padding size to use with posix_memalign]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdlib.h> +int +main () +{ + int i, bad; + void *p, *prev; + size_t padsz, blksz = 1024; + + for (padsz = 0; padsz <= 64; padsz += sizeof (long)) + { + bad = 0; + + /* Test asks for BLKSZ - PADSZ bytes aligned at BLKSZ boundary. */ + for (i = 0, prev = NULL; i < 16; i++) + { + if (posix_memalign (&p, blksz, blksz - padsz)) + bad++; + else if (prev && p - prev > blksz) + bad++; + prev = p; + } + + /* Zero means posix_memalign looks good enough with this PADSZ. */ + if (!bad) + return padsz; + } + + /* No suitable PADSZ was found, posix_memalign isn't good enough for us. */ + return 255; +}]])], emacs_cv_posix_memalign_pad=255, emacs_cv_posix_memalign_pad=$?, + emacs_cv_posix_memalign_pad=255) + if test $emacs_cv_posix_memalign_pad -le 64; then + AC_MSG_RESULT($emacs_cv_posix_memalign_pad) + AC_DEFINE(POSIX_MEMALIGN_WORKS, 1, + [Define to 1 if you have good enough `posix_memalign' function.]) + AC_DEFINE_UNQUOTED(BLOCK_PADDING, $emacs_cv_posix_memalign_pad, + [Block padding used to allocate aligned blocks.]) + else + AC_MSG_RESULT([not detected, posix_memalign will not be used]) + fi +fi + +dnl If posix_memalign isn't available or tends to create holes +dnl between blocks, check whether memalign performs better. +if test "$emacs_cv_posix_memalign_pad" -gt 64; then + if test "$ac_cv_func_memalign" = yes; then + AC_MSG_CHECKING([for block padding size for memalign]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <malloc.h> +int +main () +{ + int i, bad; + void *p, *prev; + size_t padsz, blksz = 1024; + + for (padsz = 0; padsz <= 64; padsz += sizeof (long)) + { + bad = 0; + + /* Test asks for BLKSZ - PADSZ bytes aligned at BLKSZ boundary. */ + for (i = 0, prev = NULL; i < 16; i++) + { + p = memalign (blksz, blksz - padsz); + if (!p) + bad++; + else if (prev && p - prev > blksz) + bad++; + prev = p; + } + + /* Zero means memalign looks good enough with this PADSZ. */ + if (!bad) + return padsz; + } + + /* No suitable PADSZ was found, memalign isn't good enough for us. */ + return 255; +}]])], emacs_cv_memalign_pad=255, emacs_cv_memalign_pad=$?, + emacs_cv_memalign_pad=255) + if test $emacs_cv_memalign_pad -le 64; then + AC_MSG_RESULT($emacs_cv_memalign_pad) + AC_DEFINE(MEMALIGN_WORKS, 1, + [Define to 1 if you have good enough `memalign' function.]) + AC_DEFINE_UNQUOTED(BLOCK_PADDING, $emacs_cv_memalign_pad, + [Block padding used to allocate aligned blocks.]) + else + AC_MSG_RESULT([not detected, memalign will not be used]) + fi + fi +fi + dnl Cannot use AC_CHECK_FUNCS AC_CACHE_CHECK([for __builtin_unwind_init], emacs_cv_func___builtin_unwind_init, === modified file 'src/alloc.c' --- src/alloc.c 2012-07-03 16:35:53 +0000 +++ src/alloc.c 2012-07-04 08:33:52 +0000 @@ -308,9 +308,6 @@ MEM_TYPE_VECTOR_BLOCK }; -static void *lisp_malloc (size_t, enum mem_type); - - #if GC_MARK_STACK || defined GC_MALLOC_CHECK #if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES @@ -390,7 +387,6 @@ #define MEM_NIL &mem_z static struct Lisp_Vector *allocate_vectorlike (ptrdiff_t); -static void lisp_free (void *); static void mark_stack (void); static int live_vector_p (struct mem_node *, void *); static int live_buffer_p (struct mem_node *, void *); @@ -887,15 +883,33 @@ return Qnil; } +#if ! USE_LSB_TAG + +/* Used to catch invalid address when debugging. */ + +void *lisp_malloc_loser EXTERNALLY_VISIBLE; + +/* Nonzero if the memory at ADDR can be + addressed thru a Lisp object's pointer. */ + +static inline int +verify_address (char *addr) +{ + Lisp_Object obj; + + XSETCONS (obj, addr); + if ((char *) XCONS (obj) == addr) + return 1; + lisp_malloc_loser = addr; + return 0; +} + +#endif /* not USE_LSB_TAG */ /* Like malloc but used for allocating Lisp data. NBYTES is the number of bytes to allocate, TYPE describes the intended use of the allocated memory block (for strings, for conses, ...). */ -#if ! USE_LSB_TAG -void *lisp_malloc_loser EXTERNALLY_VISIBLE; -#endif - static void * lisp_malloc (size_t nbytes, enum mem_type type) { @@ -907,24 +921,33 @@ allocated_mem_type = type; #endif +#ifdef DOUG_LEA_MALLOC + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); +#endif val = (void *) malloc (nbytes); +#ifdef DOUG_LEA_MALLOC + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); +#endif + + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } #if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, - that's equivalent to running out of memory. */ - if (val && type != MEM_TYPE_NON_LISP) + if (val && type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) { - Lisp_Object tem; - XSETCONS (tem, (char *) val + nbytes - 1); - if ((char *) XCONS (tem) != (char *) val + nbytes - 1) - { - lisp_malloc_loser = val; - free (val); - val = 0; - } + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); } -#endif +#endif /* not USE_LSB_TAG */ #if GC_MARK_STACK && !defined GC_MALLOC_CHECK if (val && type != MEM_TYPE_NON_LISP) @@ -932,8 +955,6 @@ #endif MALLOC_UNBLOCK_INPUT; - if (!val && nbytes) - memory_full (nbytes); return val; } @@ -951,30 +972,33 @@ MALLOC_UNBLOCK_INPUT; } -/***** Allocation of aligned blocks of memory to store Lisp data. *****/ - -/* The entry point is lisp_align_malloc which returns blocks of at most - BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */ - -#if defined (HAVE_POSIX_MEMALIGN) && defined (SYSTEM_MALLOC) -#define USE_POSIX_MEMALIGN 1 -#endif +/* Allocation of aligned blocks is somewhat tricky. If posix_memalign is + available, configure tries to determine the block padding value to help + posix_memalign allocate blocks of (1024 - padding) bytes without holes + between them. If suitable padding is found, we define POSIX_MEMALIGN_WORKS, + BLOCK_PADDING to padding value and use posix_memalign and free. Some + systems lacks posix_memalign, but provides memalign; for such a system, + configure performs similar check for memalign. If suitable padding is + found, we define MEMALIGN_WORKS, BLOCK_PADDING to padding value and use + memalign and free. If none of the above, we use internal_align_alloc and + internal_align_free. */ /* BLOCK_ALIGN has to be a power of 2. */ + #define BLOCK_ALIGN (1 << 10) -/* Padding to leave at the end of a malloc'd block. This is to give - malloc a chance to minimize the amount of memory wasted to alignment. - It should be tuned to the particular malloc library used. - On glibc-2.3.2, malloc never tries to align, so a padding of 0 is best. - posix_memalign on the other hand would ideally prefer a value of 4 - because otherwise, there's 1020 bytes wasted between each ablocks. - In Emacs, testing shows that those 1020 can most of the time be - efficiently used by malloc to place other objects, so a value of 0 can - still preferable unless you have a lot of aligned blocks and virtually - nothing else. */ +#if ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS + +/* Here we assume that malloc implementation has + nothing about aligned blocks management. */ + +/* Padding to leave at the end of a malloc'd block. */ + #define BLOCK_PADDING 0 -#define BLOCK_BYTES \ + +/* Maximum amount of memory in aligned block. */ + +#define BLOCK_BYTES \ (BLOCK_ALIGN - sizeof (struct ablocks *) - BLOCK_PADDING) /* Internal data structures and constants. */ @@ -982,6 +1006,7 @@ #define ABLOCKS_SIZE 16 /* An aligned block of memory. */ + struct ablock { union @@ -1007,12 +1032,14 @@ }; /* A bunch of consecutive aligned blocks. */ + struct ablocks { struct ablock blocks[ABLOCKS_SIZE]; }; -/* Size of the block requested from malloc or posix_memalign. */ +/* Size of the block requested from underlying malloc. */ + #define ABLOCKS_BYTES (sizeof (struct ablocks) - BLOCK_PADDING) #define ABLOCK_ABASE(block) \ @@ -1021,94 +1048,43 @@ : (block)->abase) /* Virtual `busy' field. */ + #define ABLOCKS_BUSY(abase) ((abase)->blocks[0].abase) /* Pointer to the (not necessarily aligned) malloc block. */ -#ifdef USE_POSIX_MEMALIGN -#define ABLOCKS_BASE(abase) (abase) -#else -#define ABLOCKS_BASE(abase) \ + +#define ABLOCKS_BASE(abase) \ (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void**)abase)[-1]) -#endif /* The list of free ablock. */ + static struct ablock *free_ablock; -/* Allocate an aligned block of nbytes. - Alignment is on a multiple of BLOCK_ALIGN and `nbytes' has to be - smaller or equal to BLOCK_BYTES. */ +/* Allocate an aligned block of NBYTES. */ + static void * -lisp_align_malloc (size_t nbytes, enum mem_type type) +internal_align_alloc (size_t nbytes) { void *base, *val; struct ablocks *abase; eassert (nbytes <= BLOCK_BYTES); - MALLOC_BLOCK_INPUT; - -#ifdef GC_MALLOC_CHECK - allocated_mem_type = type; -#endif - if (!free_ablock) { int i; intptr_t aligned; /* int gets warning casting to 64-bit pointer. */ -#ifdef DOUG_LEA_MALLOC - /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed - because mapped region contents are not preserved in - a dumped Emacs. */ - mallopt (M_MMAP_MAX, 0); -#endif - -#ifdef USE_POSIX_MEMALIGN - { - int err = posix_memalign (&base, BLOCK_ALIGN, ABLOCKS_BYTES); - if (err) - base = NULL; - abase = base; - } -#else base = malloc (ABLOCKS_BYTES); abase = ALIGN (base, BLOCK_ALIGN); -#endif - if (base == 0) - { - MALLOC_UNBLOCK_INPUT; - memory_full (ABLOCKS_BYTES); - } + if (base == NULL) + return base; aligned = (base == abase); if (!aligned) ((void**)abase)[-1] = base; -#ifdef DOUG_LEA_MALLOC - /* Back to a reasonable maximum of mmap'ed areas. */ - mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); -#endif - -#if ! USE_LSB_TAG - /* If the memory just allocated cannot be addressed thru a Lisp - object's pointer, and it needs to be, that's equivalent to - running out of memory. */ - if (type != MEM_TYPE_NON_LISP) - { - Lisp_Object tem; - char *end = (char *) base + ABLOCKS_BYTES - 1; - XSETCONS (tem, end); - if ((char *) XCONS (tem) != end) - { - lisp_malloc_loser = base; - free (base); - MALLOC_UNBLOCK_INPUT; - memory_full (SIZE_MAX); - } - } -#endif - /* Initialize the blocks and put them on the free list. If `base' was not properly aligned, we can't use the last block. */ for (i = 0; i < (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1); i++) @@ -1132,27 +1108,15 @@ val = free_ablock; free_ablock = free_ablock->x.next_free; -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK - if (type != MEM_TYPE_NON_LISP) - mem_insert (val, (char *) val + nbytes, type); -#endif - - MALLOC_UNBLOCK_INPUT; - - eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); return val; } static void -lisp_align_free (void *block) +internal_align_free (void *block) { struct ablock *ablock = block; struct ablocks *abase = ABLOCK_ABASE (ablock); - MALLOC_BLOCK_INPUT; -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK - mem_delete (mem_find (block)); -#endif /* Put on free list. */ ablock->x.next_free = free_ablock; free_ablock = ablock; @@ -1178,11 +1142,103 @@ } eassert ((aligned & 1) == aligned); eassert (i == (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1)); -#ifdef USE_POSIX_MEMALIGN - eassert ((uintptr_t) ABLOCKS_BASE (abase) % BLOCK_ALIGN == 0); -#endif free (ABLOCKS_BASE (abase)); } +} + +#else /* ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS */ + +/* Here we assume that malloc implementation has either posix_memalign or + memalign, and suitable BLOCK_PADDING value was detected by configure. */ + +#ifndef BLOCK_PADDING +#error "unknown BLOCK_PADDING" +#endif + +/* Maximum amount of memory in aligned block. */ + +#define BLOCK_BYTES (BLOCK_ALIGN - BLOCK_PADDING) + +#endif /* ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS */ + +/* Like lisp_malloc, but allocates aligned block of at + most BLOCK_BYTES aligned on a BLOCK_ALIGN boundary. */ + +static void * +lisp_align_malloc (size_t nbytes, enum mem_type type) +{ + void *val; + + eassert (nbytes <= BLOCK_BYTES); + + MALLOC_BLOCK_INPUT; + +#ifdef GC_MALLOC_CHECK + allocated_mem_type = type; +#endif + +#ifdef DOUG_LEA_MALLOC + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed + because mapped region contents are not preserved in + a dumped Emacs. */ + mallopt (M_MMAP_MAX, 0); +#endif + +#ifdef POSIX_MEMALIGN_WORKS + if (posix_memalign (&val, BLOCK_ALIGN, nbytes)) + val = NULL; +#elif MEMALIGN_WORKS + val = memalign (BLOCK_ALIGN, nbytes); +#else + val = internal_align_alloc (nbytes); +#endif + +#ifdef DOUG_LEA_MALLOC + /* Back to a reasonable maximum of mmap'ed areas. */ + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); +#endif + + if (!val && nbytes) + { + MALLOC_UNBLOCK_INPUT; + memory_full (nbytes); + } + +#if ! USE_LSB_TAG + if (type != MEM_TYPE_NON_LISP + && !verify_address ((char *) val + nbytes - 1)) + { + free (val); + MALLOC_UNBLOCK_INPUT; + memory_full (SIZE_MAX); + } +#endif /* not USE_LSB_TAG */ + +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK + if (type != MEM_TYPE_NON_LISP) + mem_insert (val, (char *) val + nbytes, type); +#endif + + MALLOC_UNBLOCK_INPUT; + eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); + return val; +} + +/* Free aligned BLOCK. This must be called to free + memory allocated with a call to lisp_align_malloc. */ + +static void +lisp_align_free (void *block) +{ + MALLOC_BLOCK_INPUT; +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK + mem_delete (mem_find (block)); +#endif +#if POSIX_MEMALIGN_WORKS || MEMALIGN_WORKS + free (block); +#else + internal_align_free (block); +#endif MALLOC_UNBLOCK_INPUT; } @@ -6661,8 +6717,10 @@ pure_bytes_used_lisp = pure_bytes_used_non_lisp = 0; pure_bytes_used_before_overflow = 0; +#if ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS /* Initialize the list of free aligned blocks. */ free_ablock = NULL; +#endif #if GC_MARK_STACK || defined GC_MALLOC_CHECK mem_init (); ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Old topic(s) again [was: Re: Aligned blocks management: obsolete?] 2012-07-04 8:39 ` Old topic(s) again [was: Re: Aligned blocks management: obsolete?] Dmitry Antipov @ 2012-07-04 13:11 ` Stefan Monnier 0 siblings, 0 replies; 25+ messages in thread From: Stefan Monnier @ 2012-07-04 13:11 UTC (permalink / raw) To: Dmitry Antipov; +Cc: emacs-devel > Likewise. Similarly, I'm not convinced it's an improvement. Overall, it doesn't seem to simplify the code and I don't see any evidence that it will have a non-negligible effect on CPU/memory use. Stefan > Dmitry > === modified file 'configure.in' > --- configure.in 2012-07-04 08:07:26 +0000 > +++ configure.in 2012-07-04 08:28:33 +0000 > @@ -2704,12 +2704,106 @@ > __fpending strsignal setitimer \ > sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \ > gai_strerror mkstemp getline getdelim fsync sync \ > -difftime posix_memalign \ > +difftime memalign posix_memalign \ > getpwent endpwent getgrent endgrent \ > touchlock \ > strcasecmp strncasecmp \ > cfmakeraw cfsetspeed copysign __executable_start) > +dnl Check whether posix_memalign can allocate blocks consecutively. > +if test "$ac_cv_func_posix_memalign" = yes; then > + AC_MSG_CHECKING([for block padding size to use with posix_memalign]) > + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdlib.h> > +int > +main () > +{ > + int i, bad; > + void *p, *prev; > + size_t padsz, blksz = 1024; > + > + for (padsz = 0; padsz <= 64; padsz += sizeof (long)) > + { > + bad = 0; > + > + /* Test asks for BLKSZ - PADSZ bytes aligned at BLKSZ boundary. */ > + for (i = 0, prev = NULL; i < 16; i++) > + { > + if (posix_memalign (&p, blksz, blksz - padsz)) > + bad++; > + else if (prev && p - prev > blksz) > + bad++; > + prev = p; > + } > + > + /* Zero means posix_memalign looks good enough with this PADSZ. */ > + if (!bad) > + return padsz; > + } > + > + /* No suitable PADSZ was found, posix_memalign isn't good enough for us. */ > + return 255; > +}]])], emacs_cv_posix_memalign_pad=255, emacs_cv_posix_memalign_pad=$?, > + emacs_cv_posix_memalign_pad=255) > + if test $emacs_cv_posix_memalign_pad -le 64; then > + AC_MSG_RESULT($emacs_cv_posix_memalign_pad) > + AC_DEFINE(POSIX_MEMALIGN_WORKS, 1, > + [Define to 1 if you have good enough `posix_memalign' function.]) > + AC_DEFINE_UNQUOTED(BLOCK_PADDING, $emacs_cv_posix_memalign_pad, > + [Block padding used to allocate aligned blocks.]) > + else > + AC_MSG_RESULT([not detected, posix_memalign will not be used]) > + fi > +fi > + > +dnl If posix_memalign isn't available or tends to create holes > +dnl between blocks, check whether memalign performs better. > +if test "$emacs_cv_posix_memalign_pad" -gt 64; then > + if test "$ac_cv_func_memalign" = yes; then > + AC_MSG_CHECKING([for block padding size for memalign]) > + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <malloc.h> > +int > +main () > +{ > + int i, bad; > + void *p, *prev; > + size_t padsz, blksz = 1024; > + > + for (padsz = 0; padsz <= 64; padsz += sizeof (long)) > + { > + bad = 0; > + > + /* Test asks for BLKSZ - PADSZ bytes aligned at BLKSZ boundary. */ > + for (i = 0, prev = NULL; i < 16; i++) > + { > + p = memalign (blksz, blksz - padsz); > + if (!p) > + bad++; > + else if (prev && p - prev > blksz) > + bad++; > + prev = p; > + } > + > + /* Zero means memalign looks good enough with this PADSZ. */ > + if (!bad) > + return padsz; > + } > + > + /* No suitable PADSZ was found, memalign isn't good enough for us. */ > + return 255; > +}]])], emacs_cv_memalign_pad=255, emacs_cv_memalign_pad=$?, > + emacs_cv_memalign_pad=255) > + if test $emacs_cv_memalign_pad -le 64; then > + AC_MSG_RESULT($emacs_cv_memalign_pad) > + AC_DEFINE(MEMALIGN_WORKS, 1, > + [Define to 1 if you have good enough `memalign' function.]) > + AC_DEFINE_UNQUOTED(BLOCK_PADDING, $emacs_cv_memalign_pad, > + [Block padding used to allocate aligned blocks.]) > + else > + AC_MSG_RESULT([not detected, memalign will not be used]) > + fi > + fi > +fi > + > dnl Cannot use AC_CHECK_FUNCS > AC_CACHE_CHECK([for __builtin_unwind_init], > emacs_cv_func___builtin_unwind_init, > === modified file 'src/alloc.c' > --- src/alloc.c 2012-07-03 16:35:53 +0000 > +++ src/alloc.c 2012-07-04 08:33:52 +0000 > @@ -308,9 +308,6 @@ > MEM_TYPE_VECTOR_BLOCK > }; > -static void *lisp_malloc (size_t, enum mem_type); > - > - > #if GC_MARK_STACK || defined GC_MALLOC_CHECK > #if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES > @@ -390,7 +387,6 @@ > #define MEM_NIL &mem_z > static struct Lisp_Vector *allocate_vectorlike (ptrdiff_t); > -static void lisp_free (void *); > static void mark_stack (void); > static int live_vector_p (struct mem_node *, void *); > static int live_buffer_p (struct mem_node *, void *); > @@ -887,15 +883,33 @@ > return Qnil; > } > +#if ! USE_LSB_TAG > + > +/* Used to catch invalid address when debugging. */ > + > +void *lisp_malloc_loser EXTERNALLY_VISIBLE; > + > +/* Nonzero if the memory at ADDR can be > + addressed thru a Lisp object's pointer. */ > + > +static inline int > +verify_address (char *addr) > +{ > + Lisp_Object obj; > + > + XSETCONS (obj, addr); > + if ((char *) XCONS (obj) == addr) > + return 1; > + lisp_malloc_loser = addr; > + return 0; > +} > + > +#endif /* not USE_LSB_TAG */ > /* Like malloc but used for allocating Lisp data. NBYTES is the > number of bytes to allocate, TYPE describes the intended use of the > allocated memory block (for strings, for conses, ...). */ > -#if ! USE_LSB_TAG > -void *lisp_malloc_loser EXTERNALLY_VISIBLE; > -#endif > - > static void * > lisp_malloc (size_t nbytes, enum mem_type type) > { > @@ -907,24 +921,33 @@ > allocated_mem_type = type; > #endif > +#ifdef DOUG_LEA_MALLOC > + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed > + because mapped region contents are not preserved in > + a dumped Emacs. */ > + mallopt (M_MMAP_MAX, 0); > +#endif > val = (void *) malloc (nbytes); > +#ifdef DOUG_LEA_MALLOC > + /* Back to a reasonable maximum of mmap'ed areas. */ > + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); > +#endif > + > + if (!val && nbytes) > + { > + MALLOC_UNBLOCK_INPUT; > + memory_full (nbytes); > + } > #if ! USE_LSB_TAG > - /* If the memory just allocated cannot be addressed thru a Lisp > - object's pointer, and it needs to be, > - that's equivalent to running out of memory. */ > - if (val && type != MEM_TYPE_NON_LISP) > + if (val && type != MEM_TYPE_NON_LISP > + && !verify_address ((char *) val + nbytes - 1)) > { > - Lisp_Object tem; > - XSETCONS (tem, (char *) val + nbytes - 1); > - if ((char *) XCONS (tem) != (char *) val + nbytes - 1) > - { > - lisp_malloc_loser = val; > - free (val); > - val = 0; > - } > + free (val); > + MALLOC_UNBLOCK_INPUT; > + memory_full (SIZE_MAX); > } > -#endif > +#endif /* not USE_LSB_TAG */ > #if GC_MARK_STACK && !defined GC_MALLOC_CHECK > if (val && type != MEM_TYPE_NON_LISP) > @@ -932,8 +955,6 @@ > #endif > MALLOC_UNBLOCK_INPUT; > - if (!val && nbytes) > - memory_full (nbytes); > return val; > } > @@ -951,30 +972,33 @@ > MALLOC_UNBLOCK_INPUT; > } > -/***** Allocation of aligned blocks of memory to store Lisp data. *****/ > - > -/* The entry point is lisp_align_malloc which returns blocks of at most > - BLOCK_BYTES and guarantees they are aligned on a BLOCK_ALIGN boundary. */ > - > -#if defined (HAVE_POSIX_MEMALIGN) && defined (SYSTEM_MALLOC) > -#define USE_POSIX_MEMALIGN 1 > -#endif > +/* Allocation of aligned blocks is somewhat tricky. If posix_memalign is > + available, configure tries to determine the block padding value to help > + posix_memalign allocate blocks of (1024 - padding) bytes without holes > + between them. If suitable padding is found, we define POSIX_MEMALIGN_WORKS, > + BLOCK_PADDING to padding value and use posix_memalign and free. Some > + systems lacks posix_memalign, but provides memalign; for such a system, > + configure performs similar check for memalign. If suitable padding is > + found, we define MEMALIGN_WORKS, BLOCK_PADDING to padding value and use > + memalign and free. If none of the above, we use internal_align_alloc and > + internal_align_free. */ > /* BLOCK_ALIGN has to be a power of 2. */ > + > #define BLOCK_ALIGN (1 << 10) > -/* Padding to leave at the end of a malloc'd block. This is to give > - malloc a chance to minimize the amount of memory wasted to alignment. > - It should be tuned to the particular malloc library used. > - On glibc-2.3.2, malloc never tries to align, so a padding of 0 is best. > - posix_memalign on the other hand would ideally prefer a value of 4 > - because otherwise, there's 1020 bytes wasted between each ablocks. > - In Emacs, testing shows that those 1020 can most of the time be > - efficiently used by malloc to place other objects, so a value of 0 can > - still preferable unless you have a lot of aligned blocks and virtually > - nothing else. */ > +#if ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS > + > +/* Here we assume that malloc implementation has > + nothing about aligned blocks management. */ > + > +/* Padding to leave at the end of a malloc'd block. */ > + > #define BLOCK_PADDING 0 > -#define BLOCK_BYTES \ > + > +/* Maximum amount of memory in aligned block. */ > + > +#define BLOCK_BYTES \ > (BLOCK_ALIGN - sizeof (struct ablocks *) - BLOCK_PADDING) > /* Internal data structures and constants. */ > @@ -982,6 +1006,7 @@ > #define ABLOCKS_SIZE 16 > /* An aligned block of memory. */ > + > struct ablock > { > union > @@ -1007,12 +1032,14 @@ > }; > /* A bunch of consecutive aligned blocks. */ > + > struct ablocks > { > struct ablock blocks[ABLOCKS_SIZE]; > }; > -/* Size of the block requested from malloc or posix_memalign. */ > +/* Size of the block requested from underlying malloc. */ > + > #define ABLOCKS_BYTES (sizeof (struct ablocks) - BLOCK_PADDING) > #define ABLOCK_ABASE(block) \ > @@ -1021,94 +1048,43 @@ > : (block)->abase) > /* Virtual `busy' field. */ > + > #define ABLOCKS_BUSY(abase) ((abase)->blocks[0].abase) > /* Pointer to the (not necessarily aligned) malloc block. */ > -#ifdef USE_POSIX_MEMALIGN > -#define ABLOCKS_BASE(abase) (abase) > -#else > -#define ABLOCKS_BASE(abase) \ > + > +#define ABLOCKS_BASE(abase) \ > (1 & (intptr_t) ABLOCKS_BUSY (abase) ? abase : ((void**)abase)[-1]) > -#endif > /* The list of free ablock. */ > + > static struct ablock *free_ablock; > -/* Allocate an aligned block of nbytes. > - Alignment is on a multiple of BLOCK_ALIGN and `nbytes' has to be > - smaller or equal to BLOCK_BYTES. */ > +/* Allocate an aligned block of NBYTES. */ > + > static void * > -lisp_align_malloc (size_t nbytes, enum mem_type type) > +internal_align_alloc (size_t nbytes) > { > void *base, *val; > struct ablocks *abase; > eassert (nbytes <= BLOCK_BYTES); > - MALLOC_BLOCK_INPUT; > - > -#ifdef GC_MALLOC_CHECK > - allocated_mem_type = type; > -#endif > - > if (!free_ablock) > { > int i; > intptr_t aligned; /* int gets warning casting to 64-bit pointer. */ > -#ifdef DOUG_LEA_MALLOC > - /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed > - because mapped region contents are not preserved in > - a dumped Emacs. */ > - mallopt (M_MMAP_MAX, 0); > -#endif > - > -#ifdef USE_POSIX_MEMALIGN > - { > - int err = posix_memalign (&base, BLOCK_ALIGN, ABLOCKS_BYTES); > - if (err) > - base = NULL; > - abase = base; > - } > -#else > base = malloc (ABLOCKS_BYTES); > abase = ALIGN (base, BLOCK_ALIGN); > -#endif > - if (base == 0) > - { > - MALLOC_UNBLOCK_INPUT; > - memory_full (ABLOCKS_BYTES); > - } > + if (base == NULL) > + return base; > aligned = (base == abase); > if (!aligned) > ((void**)abase)[-1] = base; > -#ifdef DOUG_LEA_MALLOC > - /* Back to a reasonable maximum of mmap'ed areas. */ > - mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); > -#endif > - > -#if ! USE_LSB_TAG > - /* If the memory just allocated cannot be addressed thru a Lisp > - object's pointer, and it needs to be, that's equivalent to > - running out of memory. */ > - if (type != MEM_TYPE_NON_LISP) > - { > - Lisp_Object tem; > - char *end = (char *) base + ABLOCKS_BYTES - 1; > - XSETCONS (tem, end); > - if ((char *) XCONS (tem) != end) > - { > - lisp_malloc_loser = base; > - free (base); > - MALLOC_UNBLOCK_INPUT; > - memory_full (SIZE_MAX); > - } > - } > -#endif > - > /* Initialize the blocks and put them on the free list. > If `base' was not properly aligned, we can't use the last block. */ > for (i = 0; i < (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1); i++) > @@ -1132,27 +1108,15 @@ > val = free_ablock; > free_ablock = free_ablock->x.next_free; > -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK > - if (type != MEM_TYPE_NON_LISP) > - mem_insert (val, (char *) val + nbytes, type); > -#endif > - > - MALLOC_UNBLOCK_INPUT; > - > - eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); > return val; > } > static void > -lisp_align_free (void *block) > +internal_align_free (void *block) > { > struct ablock *ablock = block; > struct ablocks *abase = ABLOCK_ABASE (ablock); > - MALLOC_BLOCK_INPUT; > -#if GC_MARK_STACK && !defined GC_MALLOC_CHECK > - mem_delete (mem_find (block)); > -#endif > /* Put on free list. */ ablock-> x.next_free = free_ablock; > free_ablock = ablock; > @@ -1178,11 +1142,103 @@ > } > eassert ((aligned & 1) == aligned); > eassert (i == (aligned ? ABLOCKS_SIZE : ABLOCKS_SIZE - 1)); > -#ifdef USE_POSIX_MEMALIGN > - eassert ((uintptr_t) ABLOCKS_BASE (abase) % BLOCK_ALIGN == 0); > -#endif > free (ABLOCKS_BASE (abase)); > } > +} > + > +#else /* ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS */ > + > +/* Here we assume that malloc implementation has either posix_memalign or > + memalign, and suitable BLOCK_PADDING value was detected by configure. */ > + > +#ifndef BLOCK_PADDING > +#error "unknown BLOCK_PADDING" > +#endif > + > +/* Maximum amount of memory in aligned block. */ > + > +#define BLOCK_BYTES (BLOCK_ALIGN - BLOCK_PADDING) > + > +#endif /* ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS */ > + > +/* Like lisp_malloc, but allocates aligned block of at > + most BLOCK_BYTES aligned on a BLOCK_ALIGN boundary. */ > + > +static void * > +lisp_align_malloc (size_t nbytes, enum mem_type type) > +{ > + void *val; > + > + eassert (nbytes <= BLOCK_BYTES); > + > + MALLOC_BLOCK_INPUT; > + > +#ifdef GC_MALLOC_CHECK > + allocated_mem_type = type; > +#endif > + > +#ifdef DOUG_LEA_MALLOC > + /* Prevent mmap'ing the chunk. Lisp data may not be mmap'ed > + because mapped region contents are not preserved in > + a dumped Emacs. */ > + mallopt (M_MMAP_MAX, 0); > +#endif > + > +#ifdef POSIX_MEMALIGN_WORKS > + if (posix_memalign (&val, BLOCK_ALIGN, nbytes)) > + val = NULL; > +#elif MEMALIGN_WORKS > + val = memalign (BLOCK_ALIGN, nbytes); > +#else > + val = internal_align_alloc (nbytes); > +#endif > + > +#ifdef DOUG_LEA_MALLOC > + /* Back to a reasonable maximum of mmap'ed areas. */ > + mallopt (M_MMAP_MAX, MMAP_MAX_AREAS); > +#endif > + > + if (!val && nbytes) > + { > + MALLOC_UNBLOCK_INPUT; > + memory_full (nbytes); > + } > + > +#if ! USE_LSB_TAG > + if (type != MEM_TYPE_NON_LISP > + && !verify_address ((char *) val + nbytes - 1)) > + { > + free (val); > + MALLOC_UNBLOCK_INPUT; > + memory_full (SIZE_MAX); > + } > +#endif /* not USE_LSB_TAG */ > + > +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK > + if (type != MEM_TYPE_NON_LISP) > + mem_insert (val, (char *) val + nbytes, type); > +#endif > + > + MALLOC_UNBLOCK_INPUT; > + eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); > + return val; > +} > + > +/* Free aligned BLOCK. This must be called to free > + memory allocated with a call to lisp_align_malloc. */ > + > +static void > +lisp_align_free (void *block) > +{ > + MALLOC_BLOCK_INPUT; > +#if GC_MARK_STACK && !defined GC_MALLOC_CHECK > + mem_delete (mem_find (block)); > +#endif > +#if POSIX_MEMALIGN_WORKS || MEMALIGN_WORKS > + free (block); > +#else > + internal_align_free (block); > +#endif > MALLOC_UNBLOCK_INPUT; > } > @@ -6661,8 +6717,10 @@ > pure_bytes_used_lisp = pure_bytes_used_non_lisp = 0; > pure_bytes_used_before_overflow = 0; > +#if ! POSIX_MEMALIGN_WORKS && ! MEMALIGN_WORKS > /* Initialize the list of free aligned blocks. */ > free_ablock = NULL; > +#endif > #if GC_MARK_STACK || defined GC_MALLOC_CHECK > mem_init (); ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-21 4:27 ` Dmitry Antipov 2012-06-21 16:29 ` Eli Zaretskii @ 2012-06-21 21:34 ` Richard Stallman 1 sibling, 0 replies; 25+ messages in thread From: Richard Stallman @ 2012-06-21 21:34 UTC (permalink / raw) To: Dmitry Antipov; +Cc: eliz, eggert, monnier, emacs-devel It is desirable to avoid runtime tests in the configure script for the sake of cross-compilation. -- Dr Richard Stallman President, Free Software Foundation 51 Franklin St Boston MA 02110 USA www.fsf.org www.gnu.org Skype: No way! That's nonfree (freedom-denying) software. Use Ekiga or an ordinary phone call ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 6:47 ` Dmitry Antipov 2012-06-20 12:48 ` Stefan Monnier @ 2012-06-20 17:06 ` Eli Zaretskii 2012-06-21 3:30 ` Dmitry Antipov 2012-06-20 17:59 ` Wolfgang Jenkner 2 siblings, 1 reply; 25+ messages in thread From: Eli Zaretskii @ 2012-06-20 17:06 UTC (permalink / raw) To: Dmitry Antipov; +Cc: eggert, monnier, emacs-devel > Date: Wed, 20 Jun 2012 10:47:08 +0400 > From: Dmitry Antipov <dmantipov@yandex.ru> > CC: Emacs development discussions <emacs-devel@gnu.org> > > This code tries to utilize system malloc features and falls back to legacy > aligned blocks management code if system malloc implementation is unknown, > broken, or lacks aligned allocation routines. > > It will be great if someone can help to test this on Windows, OSX, old *BSD > and other non-GNU/Linux systems What exactly would you like us to test, beyond the fact that the patched sources compile? Anyway, I don't understand the motivation for the changes you've done relative to the previous version. In particular: > +#if ! HAVE_POSIX_MEMALIGN && ! HAVE_WORKING_MEMALIGN && ! _MSC_VER Why only _MSC_VER is being tested here? This will only catch the MSVC build of Emacs, but will miss the MinGW (GCC-based) Windows build and the MS-DOS build. If you wanted to catch all of them, you should test DOS_NT instead. > +#ifdef HAVE_POSIX_MEMALIGN > + if (posix_memalign (&val, BLOCK_ALIGN, nbytes)) > + val = NULL; > +#elif HAVE_WORKING_MEMALIGN > + val = memalign (BLOCK_ALIGN, nbytes); > +#elif _MSC_VER > + /* Yes, the order of arguments is correct. */ > + val = _aligned_malloc (nbytes, BLOCK_ALIGN); > +#else > + val = internal_align_alloc (nbytes); > +#endif The _MSC_VER part is not right, IMO. The Windows build, whether MinGW or MSVC, uses gmalloc.c for its malloc implementation, with sbrk (implemented in w32heap.c) that calls directly into the VM allocation APIs, and is thus similar to mmap-based memory allocation on Posix hosts, in that it doesn't suffer from fragmentation. By contrast, _aligned_malloc is most probably a very thin wrapper around MS runtime implementation of malloc (MS documentation says "_aligned_malloc is based on malloc"), and we certainly don't want to use that malloc in Emacs, because it will be definitely prone to severe fragmentation problems. So, unless I'm missing something, the MS-Windows build (and also the MS-DOS one) should defined HAVE_POSIX_MEMALIGN and use its implementation provided by gmalloc.c. Unless, that is, you think that gmalloc's implementation of posix_memalign is not good enough, in which case we will be much better off improving it than switching to MS's malloc. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 17:06 ` Eli Zaretskii @ 2012-06-21 3:30 ` Dmitry Antipov 2012-06-21 16:23 ` Eli Zaretskii 0 siblings, 1 reply; 25+ messages in thread From: Dmitry Antipov @ 2012-06-21 3:30 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel On 06/20/2012 09:06 PM, Eli Zaretskii wrote: > So, unless I'm missing something, the MS-Windows build (and also the > MS-DOS one) should defined HAVE_POSIX_MEMALIGN and use its > implementation provided by gmalloc.c. Unless, that is, you think that > gmalloc's implementation of posix_memalign is not good enough, in > which case we will be much better off improving it than switching to > MS's malloc. Oops, I was pretty sure that native MSVC build uses MS malloc :-(. BTW, is it possible to cross-compile Emacs for MS-Windows with mingw? Dmitry ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-21 3:30 ` Dmitry Antipov @ 2012-06-21 16:23 ` Eli Zaretskii 0 siblings, 0 replies; 25+ messages in thread From: Eli Zaretskii @ 2012-06-21 16:23 UTC (permalink / raw) To: Dmitry Antipov; +Cc: emacs-devel > Date: Thu, 21 Jun 2012 07:30:49 +0400 > From: Dmitry Antipov <dmantipov@yandex.ru> > CC: emacs-devel@gnu.org > > BTW, is it possible to cross-compile Emacs for MS-Windows with mingw? You mean, on a Posix host? I doubt that, since the configure script does not support the MinGW target, and the various Makefile.in files were never tested to DTRT for MinGW. Volunteers to fix that are welcome. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 6:47 ` Dmitry Antipov 2012-06-20 12:48 ` Stefan Monnier 2012-06-20 17:06 ` Eli Zaretskii @ 2012-06-20 17:59 ` Wolfgang Jenkner 2012-06-21 3:12 ` Dmitry Antipov 2 siblings, 1 reply; 25+ messages in thread From: Wolfgang Jenkner @ 2012-06-20 17:59 UTC (permalink / raw) To: Dmitry Antipov Cc: Eli Zaretskii, Paul Eggert, Stefan Monnier, Emacs development discussions On Wed, Jun 20 2012, Dmitry Antipov wrote: > This code tries to utilize system malloc features and falls back to legacy > aligned blocks management code if system malloc implementation is unknown, > broken, or lacks aligned allocation routines. I think the following trivial fix is needed (relative to your patch): Wolfgang diff --git a/src/alloc.c b/src/alloc.c index 15cefb5..4cd3a32 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -894,7 +894,7 @@ void *lisp_malloc_loser EXTERNALLY_VISIBLE; /* Nonzero if the memory at ADDR can be addressed thru a Lisp object's pointer. */ -static inline void +static inline int verify_address (char *addr) { Lisp_Object obj; ^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-20 17:59 ` Wolfgang Jenkner @ 2012-06-21 3:12 ` Dmitry Antipov 0 siblings, 0 replies; 25+ messages in thread From: Dmitry Antipov @ 2012-06-21 3:12 UTC (permalink / raw) To: Wolfgang Jenkner; +Cc: Emacs development discussions On 06/20/2012 09:59 PM, Wolfgang Jenkner wrote: > I think the following trivial fix is needed (relative to your patch): Argh, sure. Thanks. Dmitry ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2012-06-19 16:51 ` Dmitry Antipov 2012-06-19 17:13 ` Eli Zaretskii 2012-06-19 21:34 ` Stefan Monnier @ 2012-06-20 6:53 ` Paul Eggert 2 siblings, 0 replies; 25+ messages in thread From: Paul Eggert @ 2012-06-20 6:53 UTC (permalink / raw) To: Dmitry Antipov; +Cc: emacs-devel On 06/19/2012 09:51 AM, Dmitry Antipov wrote: > this cleanup assumes that every malloc implementation > has reasonably efficient posix_memalign or memalign at least. memalign might be dicey, as Stefan noted. The gnulib documentation <www.gnu.org/software/gnulib/manual/html_node/posix_005fmemalign.html> says posix_memalign is missing on some plausible Emacs porting targets, and the gnulib implementation of pagealign_alloc says that posix_memalign often wastes an entire memory page per call (don't know the details about that). ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Aligned blocks management: obsolete? 2011-12-12 8:11 Aligned blocks management: obsolete? Dmitry Antipov 2011-12-12 13:13 ` Eli Zaretskii @ 2011-12-12 18:38 ` Stefan Monnier 1 sibling, 0 replies; 25+ messages in thread From: Stefan Monnier @ 2011-12-12 18:38 UTC (permalink / raw) To: Dmitry Antipov; +Cc: emacs-devel > Is it still required to maintain the cache of aligned blocks > to workaround poor malloc behavior? I believe an attached > example should perform well (i.e. allocate ~4K blocks without > ~4K holes between them) on top of any non-ancient glibc (no > ideas about other system malloc implementations, BTW). I'm not sure exactly what you're suggesting we change. If you're talking about BLOCK_PADDING (which we don't actually use right now since it defaults to 0), then we could probably just remove it, but I'm not sure what would be the benefit (other than the handful of source lines it removes, of course). Stefan ^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2012-07-04 13:11 UTC | newest] Thread overview: 25+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-12-12 8:11 Aligned blocks management: obsolete? Dmitry Antipov 2011-12-12 13:13 ` Eli Zaretskii 2011-12-12 14:07 ` Dmitry Antipov 2011-12-12 18:11 ` Eli Zaretskii 2011-12-12 18:27 ` Paul Eggert 2012-06-19 16:51 ` Dmitry Antipov 2012-06-19 17:13 ` Eli Zaretskii 2012-06-19 21:34 ` Stefan Monnier 2012-06-20 6:47 ` Dmitry Antipov 2012-06-20 12:48 ` Stefan Monnier 2012-06-20 13:54 ` Dmitry Antipov 2012-06-20 15:41 ` Stefan Monnier 2012-06-20 17:10 ` Eli Zaretskii 2012-06-21 4:27 ` Dmitry Antipov 2012-06-21 16:29 ` Eli Zaretskii 2012-07-04 8:39 ` Old topic(s) again [was: Re: Aligned blocks management: obsolete?] Dmitry Antipov 2012-07-04 13:11 ` Stefan Monnier 2012-06-21 21:34 ` Aligned blocks management: obsolete? Richard Stallman 2012-06-20 17:06 ` Eli Zaretskii 2012-06-21 3:30 ` Dmitry Antipov 2012-06-21 16:23 ` Eli Zaretskii 2012-06-20 17:59 ` Wolfgang Jenkner 2012-06-21 3:12 ` Dmitry Antipov 2012-06-20 6:53 ` Paul Eggert 2011-12-12 18:38 ` Stefan Monnier
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).