mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Improve performance of creating Hash object
When generate Hash object, the heap area of st_table will be always allocated in internally and seems it take a time. To improve performance of creating Hash object, this patch will reduce count of allocating heap areas for st_table by reuse them. Performance of creating Hash literal -> 1.53 times faster. [Fix GH-1766] [ruby-core:84008] [Feature #14146] ### Environment * OS : macOS 10.13.1 * CPU : 1.4 GHz Intel Core i7 * Compiler : Apple LLVM version 9.0.0 (clang-900.0.39) ### Before $ ./miniruby -v -I. -I../benchmark-ips/lib ~/tmp/bench/literal.rb ruby 2.5.0dev (2017-11-28 hash 60926) [x86_64-darwin17] Warming up -------------------------------------- Hash literal 51.544k i/100ms Calculating ------------------------------------- Hash literal 869.132k (± 1.1%) i/s - 4.381M in 5.041574s ### After $ ./miniruby -v -I. -I../benchmark-ips/lib ~/tmp/bench/literal.rb ruby 2.5.0dev (2017-11-28 hash 60926) [x86_64-darwin17] Warming up -------------------------------------- Hash literal 63.068k i/100ms Calculating ------------------------------------- Hash literal 1.328M (± 2.3%) i/s - 6.685M in 5.037861s ### Test code require 'benchmark/ips' Benchmark.ips do |x| x.report "Hash literal" do |loop| count = 0 while count < loop hash = {foo: 12, bar: 34, baz: 56} count += 1 end end end git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61309 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
73da429c36
commit
07d48bbe13
2 changed files with 60 additions and 6 deletions
1
inits.c
1
inits.c
|
@ -16,6 +16,7 @@
|
||||||
void
|
void
|
||||||
rb_call_inits(void)
|
rb_call_inits(void)
|
||||||
{
|
{
|
||||||
|
CALL(st);
|
||||||
CALL(Method);
|
CALL(Method);
|
||||||
CALL(RandomSeedCore);
|
CALL(RandomSeedCore);
|
||||||
CALL(sym);
|
CALL(sym);
|
||||||
|
|
65
st.c
65
st.c
|
@ -108,6 +108,7 @@
|
||||||
#endif
|
#endif
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include "ccan/list/list.h"
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define PREFETCH(addr, write_p) __builtin_prefetch(addr, write_p)
|
#define PREFETCH(addr, write_p) __builtin_prefetch(addr, write_p)
|
||||||
|
@ -125,6 +126,18 @@
|
||||||
#define st_assert(cond) ((void)(0 && (cond)))
|
#define st_assert(cond) ((void)(0 && (cond)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef struct _st_table_list {
|
||||||
|
st_table table;
|
||||||
|
struct list_node node;
|
||||||
|
} st_table_list;
|
||||||
|
|
||||||
|
struct _st_table_pool {
|
||||||
|
struct list_head head;
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
static struct _st_table_pool st_table_pool;
|
||||||
|
#define ST_TABLE_POOL_MAX_LENGTH 500
|
||||||
|
|
||||||
/* The type of hashes. */
|
/* The type of hashes. */
|
||||||
typedef st_index_t st_hash_t;
|
typedef st_index_t st_hash_t;
|
||||||
|
|
||||||
|
@ -548,6 +561,38 @@ stat_col(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static st_table *
|
||||||
|
get_st_table()
|
||||||
|
{
|
||||||
|
st_table_list *table;
|
||||||
|
|
||||||
|
if (!ruby_thread_has_gvl_p() || st_table_pool.length == 0) {
|
||||||
|
table = (st_table_list*)malloc(sizeof (st_table_list));
|
||||||
|
list_node_init(&table->node);
|
||||||
|
return (st_table*)table;
|
||||||
|
}
|
||||||
|
|
||||||
|
table = list_pop(&st_table_pool.head, st_table_list, node);
|
||||||
|
st_table_pool.length--;
|
||||||
|
|
||||||
|
return (st_table*)table;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pool_st_table(st_table *_table)
|
||||||
|
{
|
||||||
|
st_table_list *table = (st_table_list*)_table;
|
||||||
|
|
||||||
|
if (!ruby_thread_has_gvl_p() ||
|
||||||
|
st_table_pool.length >= ST_TABLE_POOL_MAX_LENGTH) {
|
||||||
|
free(table);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&st_table_pool.head, &table->node);
|
||||||
|
st_table_pool.length++;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create and return table with TYPE which can hold at least SIZE
|
/* Create and return table with TYPE which can hold at least SIZE
|
||||||
entries. The real number of entries which the table can hold is
|
entries. The real number of entries which the table can hold is
|
||||||
the nearest power of two for SIZE. */
|
the nearest power of two for SIZE. */
|
||||||
|
@ -571,7 +616,7 @@ st_init_table_with_size(const struct st_hash_type *type, st_index_t size)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
n = get_power2(size);
|
n = get_power2(size);
|
||||||
tab = (st_table *) malloc(sizeof (st_table));
|
tab = get_st_table();
|
||||||
tab->type = type;
|
tab->type = type;
|
||||||
tab->entry_power = n;
|
tab->entry_power = n;
|
||||||
tab->bin_power = features[n].bin_power;
|
tab->bin_power = features[n].bin_power;
|
||||||
|
@ -668,14 +713,14 @@ st_free_table(st_table *tab)
|
||||||
if (tab->bins != NULL)
|
if (tab->bins != NULL)
|
||||||
free(tab->bins);
|
free(tab->bins);
|
||||||
free(tab->entries);
|
free(tab->entries);
|
||||||
free(tab);
|
pool_st_table(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return byte size of memory allocted for table TAB. */
|
/* Return byte size of memory allocted for table TAB. */
|
||||||
size_t
|
size_t
|
||||||
st_memsize(const st_table *tab)
|
st_memsize(const st_table *tab)
|
||||||
{
|
{
|
||||||
return(sizeof(st_table)
|
return(sizeof(st_table_list)
|
||||||
+ (tab->bins == NULL ? 0 : bins_size(tab))
|
+ (tab->bins == NULL ? 0 : bins_size(tab))
|
||||||
+ get_allocated_entries(tab) * sizeof(st_table_entry));
|
+ get_allocated_entries(tab) * sizeof(st_table_entry));
|
||||||
}
|
}
|
||||||
|
@ -791,7 +836,7 @@ rebuild_table(st_table *tab)
|
||||||
tab->bins = new_tab->bins;
|
tab->bins = new_tab->bins;
|
||||||
free(tab->entries);
|
free(tab->entries);
|
||||||
tab->entries = new_tab->entries;
|
tab->entries = new_tab->entries;
|
||||||
free(new_tab);
|
pool_st_table(new_tab);
|
||||||
}
|
}
|
||||||
tab->entries_start = 0;
|
tab->entries_start = 0;
|
||||||
tab->entries_bound = tab->num_entries;
|
tab->entries_bound = tab->num_entries;
|
||||||
|
@ -1238,7 +1283,7 @@ st_copy(st_table *old_tab)
|
||||||
{
|
{
|
||||||
st_table *new_tab;
|
st_table *new_tab;
|
||||||
|
|
||||||
new_tab = (st_table *) malloc(sizeof(st_table));
|
new_tab = (st_table *) malloc(sizeof(st_table_list));
|
||||||
*new_tab = *old_tab;
|
*new_tab = *old_tab;
|
||||||
if (old_tab->bins == NULL)
|
if (old_tab->bins == NULL)
|
||||||
new_tab->bins = NULL;
|
new_tab->bins = NULL;
|
||||||
|
@ -2012,7 +2057,7 @@ st_expand_table(st_table *tab, st_index_t siz)
|
||||||
tab->entries = tmp->entries;
|
tab->entries = tmp->entries;
|
||||||
tab->bins = NULL;
|
tab->bins = NULL;
|
||||||
tab->rebuilds_num++;
|
tab->rebuilds_num++;
|
||||||
free(tmp);
|
pool_st_table(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rehash using linear search. */
|
/* Rehash using linear search. */
|
||||||
|
@ -2192,3 +2237,11 @@ rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash)
|
||||||
st_insert_generic(tab, argc, argv, hash);
|
st_insert_generic(tab, argc, argv, hash);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
Init_st(void)
|
||||||
|
{
|
||||||
|
// initialize st_table pool
|
||||||
|
list_head_init(&st_table_pool.head);
|
||||||
|
st_table_pool.length = 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue