diff options
| author | Mattias EngdegÄrd | 2023-10-27 22:15:09 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-01-13 20:50:38 +0100 |
| commit | fa5c07fc87d557e642fc325852e8d0c87a9c176e (patch) | |
| tree | c83fc3b64e9a642d9b303a19852bc484a8b46de2 /src/alloc.c | |
| parent | 49fd4d120deb0b878ad262aea7d849c7275bc12c (diff) | |
| download | emacs-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.c | 86 |
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. */ | ||
| 370 | static 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. */ | ||
| 5637 | void * | ||
| 5638 | hash_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. */ | ||
| 5648 | void | ||
| 5649 | hash_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 | } |