aboutsummaryrefslogtreecommitdiffstats
path: root/src/alloc.c
diff options
context:
space:
mode:
authorMattias EngdegÄrd2023-10-27 22:15:09 +0200
committerMattias EngdegÄrd2024-01-13 20:50:38 +0100
commitfa5c07fc87d557e642fc325852e8d0c87a9c176e (patch)
treec83fc3b64e9a642d9b303a19852bc484a8b46de2 /src/alloc.c
parent49fd4d120deb0b878ad262aea7d849c7275bc12c (diff)
downloademacs-fa5c07fc87d557e642fc325852e8d0c87a9c176e.tar.gz
emacs-fa5c07fc87d557e642fc325852e8d0c87a9c176e.zip
Use non-Lisp allocation for internal hash-table vectors
Using xmalloc for allocating these arrays is much cheaper than using Lisp vectors since they are no longer marked or swept by the GC, and deallocated much sooner. This makes GC faster and less frequent, and improves temporal locality. Zero-sized tables use NULL for their (0-length) vectors except the index vector which has size 1 and uses a shared constant static vector since it cannot be modified anyway. This makes creation and destruction of zero-sized hash tables very fast; they consume no memory outside the base object. * src/lisp.h (struct Lisp_Hash_Table): Retype the index, next, hash and key_and_value vectors from Lisp_Object to appropriately typed arrays (although hash values are still stored as Lisp fixnums). Add explicit table_size and index_size members. All users updated. * src/alloc.c (gcstat): Add total_hash_table_bytes. (hash_table_allocated_bytes): New. (cleanup_vector): Free hash table vectors when sweeping the object. (hash_table_alloc_bytes, hash_table_free_bytes): New. (sweep_vectors): Update gcstat.total_hash_table_bytes. (total_bytes_of_live_objects): Use it. (purecopy_hash_table): Adapt allocation of hash table vectors. (process_mark_stack): No more Lisp slots in the struct to trace. * src/fns.c (empty_hash_index_vector): New. (allocate_hash_table): Allocate without automatically GCed slots. (alloc_larger_vector): Remove. (make_hash_table, copy_hash_table, maybe_resize_hash_table): Adapt vector allocation and initialisation. * src/pdumper.c (hash_table_freeze, hash_table_thaw, dump_hash_table) (dump_hash_table_contents): Adapt dumping and loading to field changes.
Diffstat (limited to 'src/alloc.c')
-rw-r--r--src/alloc.c86
1 files changed, 75 insertions, 11 deletions
diff --git a/src/alloc.c b/src/alloc.c
index 636b4972c84..7432163db25 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -359,8 +359,16 @@ static struct gcstat
359 object_ct total_floats, total_free_floats; 359 object_ct total_floats, total_free_floats;
360 object_ct total_intervals, total_free_intervals; 360 object_ct total_intervals, total_free_intervals;
361 object_ct total_buffers; 361 object_ct total_buffers;
362
363 /* Size of the ancillary arrays of live hash-table objects.
364 The objects themselves are not included (counted as vectors above). */
365 byte_ct total_hash_table_bytes;
362} gcstat; 366} gcstat;
363 367
368/* Total size of ancillary arrays of all allocated hash-table objects,
369 both dead and alive. This number is always kept up-to-date. */
370static ptrdiff_t hash_table_allocated_bytes = 0;
371
364/* Points to memory space allocated as "spare", to be freed if we run 372/* Points to memory space allocated as "spare", to be freed if we run
365 out of memory. We keep one large block, four cons-blocks, and 373 out of memory. We keep one large block, four cons-blocks, and
366 two string blocks. */ 374 two string blocks. */
@@ -3430,6 +3438,23 @@ cleanup_vector (struct Lisp_Vector *vector)
3430 } 3438 }
3431#endif 3439#endif
3432 break; 3440 break;
3441 case PVEC_HASH_TABLE:
3442 {
3443 struct Lisp_Hash_Table *h = PSEUDOVEC_STRUCT (vector, Lisp_Hash_Table);
3444 if (h->table_size > 0)
3445 {
3446 eassert (h->index_size > 1);
3447 xfree (h->index);
3448 xfree (h->key_and_value);
3449 xfree (h->next);
3450 xfree (h->hash);
3451 ptrdiff_t bytes = (h->table_size * (2 * sizeof *h->key_and_value
3452 + sizeof *h->hash
3453 + sizeof *h->next)
3454 + h->index_size * sizeof *h->index);
3455 hash_table_allocated_bytes -= bytes;
3456 }
3457 }
3433 /* Keep the switch exhaustive. */ 3458 /* Keep the switch exhaustive. */
3434 case PVEC_NORMAL_VECTOR: 3459 case PVEC_NORMAL_VECTOR:
3435 case PVEC_FREE: 3460 case PVEC_FREE:
@@ -3440,7 +3465,6 @@ cleanup_vector (struct Lisp_Vector *vector)
3440 case PVEC_WINDOW: 3465 case PVEC_WINDOW:
3441 case PVEC_BOOL_VECTOR: 3466 case PVEC_BOOL_VECTOR:
3442 case PVEC_BUFFER: 3467 case PVEC_BUFFER:
3443 case PVEC_HASH_TABLE:
3444 case PVEC_TERMINAL: 3468 case PVEC_TERMINAL:
3445 case PVEC_WINDOW_CONFIGURATION: 3469 case PVEC_WINDOW_CONFIGURATION:
3446 case PVEC_OTHER: 3470 case PVEC_OTHER:
@@ -3554,6 +3578,8 @@ sweep_vectors (void)
3554 lisp_free (lv); 3578 lisp_free (lv);
3555 } 3579 }
3556 } 3580 }
3581
3582 gcstat.total_hash_table_bytes = hash_table_allocated_bytes;
3557} 3583}
3558 3584
3559/* Maximum number of elements in a vector. This is a macro so that it 3585/* Maximum number of elements in a vector. This is a macro so that it
@@ -5606,6 +5632,28 @@ valid_lisp_object_p (Lisp_Object obj)
5606 return 0; 5632 return 0;
5607} 5633}
5608 5634
5635/* Like xmalloc, but makes allocation count toward the total consing.
5636 Return NULL for a zero-sized allocation. */
5637void *
5638hash_table_alloc_bytes (ptrdiff_t nbytes)
5639{
5640 if (nbytes == 0)
5641 return NULL;
5642 tally_consing (nbytes);
5643 hash_table_allocated_bytes += nbytes;
5644 return xmalloc (nbytes);
5645}
5646
5647/* Like xfree, but makes allocation count toward the total consing. */
5648void
5649hash_table_free_bytes (void *p, ptrdiff_t nbytes)
5650{
5651 tally_consing (-nbytes);
5652 hash_table_allocated_bytes -= nbytes;
5653 xfree (p);
5654}
5655
5656
5609/*********************************************************************** 5657/***********************************************************************
5610 Pure Storage Management 5658 Pure Storage Management
5611 ***********************************************************************/ 5659 ***********************************************************************/
@@ -5897,10 +5945,28 @@ purecopy_hash_table (struct Lisp_Hash_Table *table)
5897 pure->test.name = purecopy (table->test.name); 5945 pure->test.name = purecopy (table->test.name);
5898 pure->test.user_hash_function = purecopy (table->test.user_hash_function); 5946 pure->test.user_hash_function = purecopy (table->test.user_hash_function);
5899 pure->test.user_cmp_function = purecopy (table->test.user_cmp_function); 5947 pure->test.user_cmp_function = purecopy (table->test.user_cmp_function);
5900 pure->hash = purecopy (table->hash); 5948
5901 pure->next = purecopy (table->next); 5949 if (table->table_size > 0)
5902 pure->index = purecopy (table->index); 5950 {
5903 pure->key_and_value = purecopy (table->key_and_value); 5951 ptrdiff_t hash_bytes = table->table_size * sizeof *table->hash;
5952 pure->hash = pure_alloc (hash_bytes, -(int)sizeof *table->hash);
5953 memcpy (pure->hash, table->hash, hash_bytes);
5954
5955 ptrdiff_t next_bytes = table->table_size * sizeof *table->next;
5956 pure->next = pure_alloc (next_bytes, -(int)sizeof *table->next);
5957 memcpy (pure->next, table->next, next_bytes);
5958
5959 ptrdiff_t nvalues = table->table_size * 2;
5960 ptrdiff_t kv_bytes = nvalues * sizeof *table->key_and_value;
5961 pure->key_and_value = pure_alloc (kv_bytes,
5962 -(int)sizeof *table->key_and_value);
5963 for (ptrdiff_t i = 0; i < nvalues; i++)
5964 pure->key_and_value[i] = purecopy (table->key_and_value[i]);
5965
5966 ptrdiff_t index_bytes = table->index_size * sizeof *table->index;
5967 pure->index = pure_alloc (index_bytes, -(int)sizeof *table->index);
5968 memcpy (pure->index, table->index, index_bytes);
5969 }
5904 5970
5905 return pure; 5971 return pure;
5906} 5972}
@@ -6084,6 +6150,7 @@ total_bytes_of_live_objects (void)
6084 tot += object_bytes (gcstat.total_floats, sizeof (struct Lisp_Float)); 6150 tot += object_bytes (gcstat.total_floats, sizeof (struct Lisp_Float));
6085 tot += object_bytes (gcstat.total_intervals, sizeof (struct interval)); 6151 tot += object_bytes (gcstat.total_intervals, sizeof (struct interval));
6086 tot += object_bytes (gcstat.total_strings, sizeof (struct Lisp_String)); 6152 tot += object_bytes (gcstat.total_strings, sizeof (struct Lisp_String));
6153 tot += gcstat.total_hash_table_bytes;
6087 return tot; 6154 return tot;
6088} 6155}
6089 6156
@@ -7227,23 +7294,20 @@ process_mark_stack (ptrdiff_t base_sp)
7227 case PVEC_HASH_TABLE: 7294 case PVEC_HASH_TABLE:
7228 { 7295 {
7229 struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr; 7296 struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr;
7230 ptrdiff_t size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK;
7231 set_vector_marked (ptr); 7297 set_vector_marked (ptr);
7232 mark_stack_push_values (ptr->contents, size);
7233 mark_stack_push_value (h->test.name); 7298 mark_stack_push_value (h->test.name);
7234 mark_stack_push_value (h->test.user_hash_function); 7299 mark_stack_push_value (h->test.user_hash_function);
7235 mark_stack_push_value (h->test.user_cmp_function); 7300 mark_stack_push_value (h->test.user_cmp_function);
7236 if (h->weakness == Weak_None) 7301 if (h->weakness == Weak_None)
7237 mark_stack_push_value (h->key_and_value); 7302 mark_stack_push_values (h->key_and_value,
7303 2 * h->table_size);
7238 else 7304 else
7239 { 7305 {
7240 /* For weak tables, mark only the vector and not its 7306 /* For weak tables, don't mark the
7241 contents --- that's what makes it weak. */ 7307 contents --- that's what makes it weak. */
7242 eassert (h->next_weak == NULL); 7308 eassert (h->next_weak == NULL);
7243 h->next_weak = weak_hash_tables; 7309 h->next_weak = weak_hash_tables;
7244 weak_hash_tables = h; 7310 weak_hash_tables = h;
7245 if (!PURE_P (h->key_and_value))
7246 set_vector_marked (XVECTOR (h->key_and_value));
7247 } 7311 }
7248 break; 7312 break;
7249 } 7313 }