aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2023-11-04 16:34:09 +0100
committerMattias EngdegÄrd2024-01-13 20:50:37 +0100
commitd3cefd3e98354929d96c9396e5920e8a123784dc (patch)
treeb0fd45c15faa88216653413229e759444810585d
parentc3d0cc50faf588479db62e20ceabe044dd89e244 (diff)
downloademacs-d3cefd3e98354929d96c9396e5920e8a123784dc.tar.gz
emacs-d3cefd3e98354929d96c9396e5920e8a123784dc.zip
Leaner hash table dumping and thawing
Only dump the actual data, and the test encoded as an enum. This simplifies dumping, makes dump files smaller and saves space at run time. * src/lisp.h (hash_table_std_test_t): New enum. (struct Lisp_Hash_Table): Add frozen_test member, consuming no extra space. * src/fns.c (hashfn_user_defined): Now static. (hash_table_test_from_std): New. (hash_table_rehash): Rename to... (hash_table_thaw): ...this and rewrite. * src/pdumper.c (hash_table_contents): Only include actual data, not unused space. (hash_table_std_test): New. (hash_table_freeze): Set frozen_test from test. (dump_hash_table): Dump frozen_test, not the whole test struct. Don't bother other dumping fields that can be derived.
-rw-r--r--src/fns.c53
-rw-r--r--src/lisp.h12
-rw-r--r--src/pdumper.c63
3 files changed, 70 insertions, 58 deletions
diff --git a/src/fns.c b/src/fns.c
index efec74d4959..74fdf29417e 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -4474,7 +4474,7 @@ hashfn_eql (Lisp_Object key, struct Lisp_Hash_Table *h)
4474/* Given H, return a hash code for KEY which uses a user-defined 4474/* Given H, return a hash code for KEY which uses a user-defined
4475 function to compare keys. */ 4475 function to compare keys. */
4476 4476
4477Lisp_Object 4477static Lisp_Object
4478hashfn_user_defined (Lisp_Object key, struct Lisp_Hash_Table *h) 4478hashfn_user_defined (Lisp_Object key, struct Lisp_Hash_Table *h)
4479{ 4479{
4480 Lisp_Object args[] = { h->test.user_hash_function, key }; 4480 Lisp_Object args[] = { h->test.user_hash_function, key };
@@ -4638,11 +4638,10 @@ maybe_resize_hash_table (struct Lisp_Hash_Table *h)
4638 if (h->next_free < 0) 4638 if (h->next_free < 0)
4639 { 4639 {
4640 ptrdiff_t old_size = HASH_TABLE_SIZE (h); 4640 ptrdiff_t old_size = HASH_TABLE_SIZE (h);
4641 EMACS_INT new_size; 4641 /* FIXME: better growth management, ditch std_rehash_size */
4642 4642 EMACS_INT new_size = old_size * std_rehash_size;
4643 double float_new_size = old_size * std_rehash_size; 4643 if (new_size < EMACS_INT_MAX)
4644 if (float_new_size < EMACS_INT_MAX) 4644 new_size = max (new_size, 32); /* avoid slow initial growth */
4645 new_size = float_new_size;
4646 else 4645 else
4647 new_size = EMACS_INT_MAX; 4646 new_size = EMACS_INT_MAX;
4648 if (PTRDIFF_MAX < new_size) 4647 if (PTRDIFF_MAX < new_size)
@@ -4691,20 +4690,39 @@ maybe_resize_hash_table (struct Lisp_Hash_Table *h)
4691 } 4690 }
4692} 4691}
4693 4692
4694/* Recompute the hashes (and hence also the "next" pointers). 4693static const struct hash_table_test *
4695 Normally there's never a need to recompute hashes. 4694hash_table_test_from_std (hash_table_std_test_t test)
4696 This is done only on first access to a hash-table loaded from 4695{
4697 the "pdump", because the objects' addresses may have changed, thus 4696 switch (test)
4698 affecting their hashes. */ 4697 {
4698 case Test_eq: return &hashtest_eq;
4699 case Test_eql: return &hashtest_eql;
4700 case Test_equal: return &hashtest_equal;
4701 }
4702 emacs_abort();
4703}
4704
4705/* Rebuild a hash table from its frozen (dumped) form. */
4699void 4706void
4700hash_table_rehash (Lisp_Object hash) 4707hash_table_thaw (Lisp_Object hash_table)
4701{ 4708{
4702 struct Lisp_Hash_Table *h = XHASH_TABLE (hash); 4709 struct Lisp_Hash_Table *h = XHASH_TABLE (hash_table);
4703 ptrdiff_t i, count = h->count; 4710
4711 /* Freezing discarded most non-essential information; recompute it.
4712 The allocation is minimal with no room for growth. */
4713 h->test = *hash_table_test_from_std (h->frozen_test);
4714 ptrdiff_t size = ASIZE (h->key_and_value) / 2;
4715 h->count = size;
4716 ptrdiff_t index_size = hash_index_size (size);
4717 h->next_free = -1;
4718
4719 h->hash = make_nil_vector (size);
4720 h->next = make_vector (size, make_fixnum (-1));
4721 h->index = make_vector (index_size, make_fixnum (-1));
4704 4722
4705 /* Recompute the actual hash codes for each entry in the table. 4723 /* Recompute the actual hash codes for each entry in the table.
4706 Order is still invalid. */ 4724 Order is still invalid. */
4707 for (i = 0; i < count; i++) 4725 for (ptrdiff_t i = 0; i < size; i++)
4708 { 4726 {
4709 Lisp_Object key = HASH_KEY (h, i); 4727 Lisp_Object key = HASH_KEY (h, i);
4710 Lisp_Object hash_code = hash_from_key (h, key); 4728 Lisp_Object hash_code = hash_from_key (h, key);
@@ -4712,12 +4730,7 @@ hash_table_rehash (Lisp_Object hash)
4712 set_hash_hash_slot (h, i, hash_code); 4730 set_hash_hash_slot (h, i, hash_code);
4713 set_hash_next_slot (h, i, HASH_INDEX (h, start_of_bucket)); 4731 set_hash_next_slot (h, i, HASH_INDEX (h, start_of_bucket));
4714 set_hash_index_slot (h, start_of_bucket, i); 4732 set_hash_index_slot (h, start_of_bucket, i);
4715 eassert (HASH_NEXT (h, i) != i); /* Stop loops. */
4716 } 4733 }
4717
4718 ptrdiff_t size = ASIZE (h->next);
4719 for (; i + 1 < size; i++)
4720 set_hash_next_slot (h, i, i + 1);
4721} 4734}
4722 4735
4723/* Lookup KEY in hash table H. If HASH is non-null, return in *HASH 4736/* Lookup KEY in hash table H. If HASH is non-null, return in *HASH
diff --git a/src/lisp.h b/src/lisp.h
index 48e1f943ed8..d9b828b0328 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -2385,6 +2385,12 @@ INLINE int
2385 2385
2386struct Lisp_Hash_Table; 2386struct Lisp_Hash_Table;
2387 2387
2388typedef enum {
2389 Test_eql,
2390 Test_eq,
2391 Test_equal,
2392} hash_table_std_test_t;
2393
2388struct hash_table_test 2394struct hash_table_test
2389{ 2395{
2390 /* Function used to compare keys; always a bare symbol. */ 2396 /* Function used to compare keys; always a bare symbol. */
@@ -2473,6 +2479,9 @@ struct Lisp_Hash_Table
2473 /* Weakness of the table. */ 2479 /* Weakness of the table. */
2474 hash_table_weakness_t weakness : 8; 2480 hash_table_weakness_t weakness : 8;
2475 2481
2482 /* Hash table test (only used when frozen in dump) */
2483 hash_table_std_test_t frozen_test : 8;
2484
2476 /* True if the table can be purecopied. The table cannot be 2485 /* True if the table can be purecopied. The table cannot be
2477 changed afterwards. */ 2486 changed afterwards. */
2478 bool purecopy; 2487 bool purecopy;
@@ -2563,7 +2572,7 @@ hash_from_key (struct Lisp_Hash_Table *h, Lisp_Object key)
2563 return h->test.hashfn (key, h); 2572 return h->test.hashfn (key, h);
2564} 2573}
2565 2574
2566void hash_table_rehash (Lisp_Object); 2575void hash_table_thaw (Lisp_Object hash_table);
2567 2576
2568/* Default size for hash tables if not specified. */ 2577/* Default size for hash tables if not specified. */
2569 2578
@@ -4038,7 +4047,6 @@ extern void hexbuf_digest (char *, void const *, int);
4038extern char *extract_data_from_object (Lisp_Object, ptrdiff_t *, ptrdiff_t *); 4047extern char *extract_data_from_object (Lisp_Object, ptrdiff_t *, ptrdiff_t *);
4039EMACS_UINT hash_string (char const *, ptrdiff_t); 4048EMACS_UINT hash_string (char const *, ptrdiff_t);
4040EMACS_UINT sxhash (Lisp_Object); 4049EMACS_UINT sxhash (Lisp_Object);
4041Lisp_Object hashfn_user_defined (Lisp_Object, struct Lisp_Hash_Table *);
4042Lisp_Object make_hash_table (struct hash_table_test, EMACS_INT, 4050Lisp_Object make_hash_table (struct hash_table_test, EMACS_INT,
4043 hash_table_weakness_t, bool); 4051 hash_table_weakness_t, bool);
4044Lisp_Object hash_table_weakness_symbol (hash_table_weakness_t weak); 4052Lisp_Object hash_table_weakness_symbol (hash_table_weakness_t weak);
diff --git a/src/pdumper.c b/src/pdumper.c
index 8072148c542..e4349f0cb17 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -2646,34 +2646,26 @@ dump_vectorlike_generic (struct dump_context *ctx,
2646 return offset; 2646 return offset;
2647} 2647}
2648 2648
2649/* Return a vector of KEY, VALUE pairs in the given hash table H. The 2649/* Return a vector of KEY, VALUE pairs in the given hash table H.
2650 first H->count pairs are valid, and the rest are unbound. */ 2650 No room for growth is included. */
2651static Lisp_Object 2651static Lisp_Object
2652hash_table_contents (struct Lisp_Hash_Table *h) 2652hash_table_contents (struct Lisp_Hash_Table *h)
2653{ 2653{
2654 if (h->test.hashfn == hashfn_user_defined) 2654 ptrdiff_t old_size = HASH_TABLE_SIZE (h);
2655 error ("cannot dump hash tables with user-defined tests"); /* Bug#36769 */ 2655 ptrdiff_t size = h->count;
2656
2657 ptrdiff_t size = HASH_TABLE_SIZE (h);
2658 Lisp_Object key_and_value = make_uninit_vector (2 * size); 2656 Lisp_Object key_and_value = make_uninit_vector (2 * size);
2659 ptrdiff_t n = 0; 2657 ptrdiff_t n = 0;
2660 2658
2661 /* Make sure key_and_value ends up in the same order; charset.c 2659 /* Make sure key_and_value ends up in the same order; charset.c
2662 relies on it by expecting hash table indices to stay constant 2660 relies on it by expecting hash table indices to stay constant
2663 across the dump. */ 2661 across the dump. */
2664 for (ptrdiff_t i = 0; i < size; i++) 2662 for (ptrdiff_t i = 0; i < old_size; i++)
2665 if (!NILP (HASH_HASH (h, i))) 2663 if (!NILP (HASH_HASH (h, i)))
2666 { 2664 {
2667 ASET (key_and_value, n++, HASH_KEY (h, i)); 2665 ASET (key_and_value, n++, HASH_KEY (h, i));
2668 ASET (key_and_value, n++, HASH_VALUE (h, i)); 2666 ASET (key_and_value, n++, HASH_VALUE (h, i));
2669 } 2667 }
2670 2668
2671 while (n < 2 * size)
2672 {
2673 ASET (key_and_value, n++, Qunbound);
2674 ASET (key_and_value, n++, Qnil);
2675 }
2676
2677 return key_and_value; 2669 return key_and_value;
2678} 2670}
2679 2671
@@ -2686,25 +2678,32 @@ dump_hash_table_list (struct dump_context *ctx)
2686 return 0; 2678 return 0;
2687} 2679}
2688 2680
2689static void 2681static hash_table_std_test_t
2690hash_table_freeze (struct Lisp_Hash_Table *h) 2682hash_table_std_test (const struct hash_table_test *t)
2691{ 2683{
2692 ptrdiff_t npairs = ASIZE (h->key_and_value) / 2; 2684 if (BASE_EQ (t->name, Qeq))
2693 h->key_and_value = hash_table_contents (h); 2685 return Test_eq;
2694 h->next = h->hash = make_fixnum (npairs); 2686 if (BASE_EQ (t->name, Qeql))
2695 h->index = make_fixnum (ASIZE (h->index)); 2687 return Test_eql;
2696 h->next_free = (npairs == h->count ? -1 : h->count); 2688 if (BASE_EQ (t->name, Qequal))
2689 return Test_equal;
2690 error ("cannot dump hash tables with user-defined tests"); /* Bug#36769 */
2697} 2691}
2698 2692
2693/* Compact contents and discard inessential information from a hash table,
2694 preparing it for dumping.
2695 See `hash_table_thaw' for the code that restores the object to a usable
2696 state. */
2699static void 2697static void
2700hash_table_thaw (Lisp_Object hash) 2698hash_table_freeze (struct Lisp_Hash_Table *h)
2701{ 2699{
2702 struct Lisp_Hash_Table *h = XHASH_TABLE (hash); 2700 h->key_and_value = hash_table_contents (h);
2703 h->hash = make_nil_vector (XFIXNUM (h->hash)); 2701 eassert (ASIZE (h->key_and_value) == h->count * 2);
2704 h->next = Fmake_vector (h->next, make_fixnum (-1)); 2702 h->next = Qnil;
2705 h->index = Fmake_vector (h->index, make_fixnum (-1)); 2703 h->hash = Qnil;
2706 2704 h->index = Qnil;
2707 hash_table_rehash (hash); 2705 h->count = 0;
2706 h->frozen_test = hash_table_std_test (&h->test);
2708} 2707}
2709 2708
2710static dump_off 2709static dump_off
@@ -2724,19 +2723,11 @@ dump_hash_table (struct dump_context *ctx, Lisp_Object object)
2724 dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header); 2723 dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header);
2725 /* TODO: dump the hash bucket vectors synchronously here to keep 2724 /* TODO: dump the hash bucket vectors synchronously here to keep
2726 them as close to the hash table as possible. */ 2725 them as close to the hash table as possible. */
2727 DUMP_FIELD_COPY (out, hash, count);
2728 DUMP_FIELD_COPY (out, hash, next_free);
2729 DUMP_FIELD_COPY (out, hash, weakness); 2726 DUMP_FIELD_COPY (out, hash, weakness);
2730 DUMP_FIELD_COPY (out, hash, purecopy); 2727 DUMP_FIELD_COPY (out, hash, purecopy);
2731 DUMP_FIELD_COPY (out, hash, mutable); 2728 DUMP_FIELD_COPY (out, hash, mutable);
2729 DUMP_FIELD_COPY (out, hash, frozen_test);
2732 dump_field_lv (ctx, out, hash, &hash->key_and_value, WEIGHT_STRONG); 2730 dump_field_lv (ctx, out, hash, &hash->key_and_value, WEIGHT_STRONG);
2733 dump_field_lv (ctx, out, hash, &hash->test.name, WEIGHT_STRONG);
2734 dump_field_lv (ctx, out, hash, &hash->test.user_hash_function,
2735 WEIGHT_STRONG);
2736 dump_field_lv (ctx, out, hash, &hash->test.user_cmp_function,
2737 WEIGHT_STRONG);
2738 dump_field_emacs_ptr (ctx, out, hash, &hash->test.cmpfn);
2739 dump_field_emacs_ptr (ctx, out, hash, &hash->test.hashfn);
2740 eassert (hash->next_weak == NULL); 2731 eassert (hash->next_weak == NULL);
2741 return finish_dump_pvec (ctx, &out->header); 2732 return finish_dump_pvec (ctx, &out->header);
2742} 2733}