Skip to content

Commit

Permalink
fix #13529, slowdown with large number of async sleep calls
Browse files Browse the repository at this point in the history
The problem was performance degradation of ObjectIdDict with many
deleted items. The table needs to be rehashed after a large number
of deletions.

(cherry picked from commit c5e0f47)
ref #17655
  • Loading branch information
JeffBezanson authored and tkelman committed Aug 11, 2016
1 parent 203a8f0 commit 032db5f
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 13 deletions.
24 changes: 19 additions & 5 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ push!(t::Associative, p::Pair, q::Pair, r::Pair...) = push!(push!(push!(t, p), q

type ObjectIdDict <: Associative{Any,Any}
ht::Vector{Any}
ObjectIdDict() = new(Vector{Any}(32))
ndel::Int
ObjectIdDict() = new(Vector{Any}(32), 0)

function ObjectIdDict(itr)
d = ObjectIdDict()
Expand All @@ -266,28 +267,41 @@ end

similar(d::ObjectIdDict) = ObjectIdDict()

function rehash!(t::ObjectIdDict, newsz = length(t.ht))
t.ht = ccall(:jl_idtable_rehash, Any, (Any, Csize_t), t.ht, newsz)
t
end

function setindex!(t::ObjectIdDict, v::ANY, k::ANY)
if t.ndel >= ((3*length(t.ht))>>2)
rehash!(t, max(length(t.ht)>>1, 32))
t.ndel = 0
end
t.ht = ccall(:jl_eqtable_put, Array{Any,1}, (Any, Any, Any), t.ht, k, v)
return t
end

get(t::ObjectIdDict, key::ANY, default::ANY) =
ccall(:jl_eqtable_get, Any, (Any, Any, Any), t.ht, key, default)

pop!(t::ObjectIdDict, key::ANY, default::ANY) =
ccall(:jl_eqtable_pop, Any, (Any, Any, Any), t.ht, key, default)
function pop!(t::ObjectIdDict, key::ANY, default::ANY)
val = ccall(:jl_eqtable_pop, Any, (Any, Any, Any), t.ht, key, default)
# TODO: this can underestimate `ndel`
val === default || (t.ndel += 1)
return val
end

function pop!(t::ObjectIdDict, key::ANY)
val = pop!(t, key, secret_table_token)
!is(val,secret_table_token) ? val : throw(KeyError(key))
end

function delete!(t::ObjectIdDict, key::ANY)
ccall(:jl_eqtable_pop, Any, (Any, Any), t.ht, key)
pop!(t, key, secret_table_token)
t
end

empty!(t::ObjectIdDict) = (t.ht = Vector{Any}(length(t.ht)); t)
empty!(t::ObjectIdDict) = (t.ht = Vector{Any}(length(t.ht)); t.ndel = 0; t)

_oidd_nextind(a, i) = reinterpret(Int,ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i))

Expand Down
2 changes: 1 addition & 1 deletion src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1815,7 +1815,7 @@ static void jl_reinit_item(jl_value_t *v, int how, arraylist_t *tracee_list)
case 1: { // rehash ObjectIdDict
jl_array_t **a = (jl_array_t**)v;
// Assume *a don't need a write barrier
jl_idtable_rehash(a, jl_array_len(*a));
*a = jl_idtable_rehash(*a, jl_array_len(*a));
jl_gc_wb(v, *a);
break;
}
Expand Down
2 changes: 1 addition & 1 deletion src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ int32_t jl_get_llvm_gv(jl_value_t *p);
int32_t jl_assign_functionID(/*llvm::Function*/void *function);
// the first argument to jl_idtable_rehash is used to return a value
// make sure it is rooted if it is used after the function returns
void jl_idtable_rehash(jl_array_t **pa, size_t newsz);
JL_DLLEXPORT jl_array_t *jl_idtable_rehash(jl_array_t *a, size_t newsz);

JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *module);
jl_lambda_info_t *jl_get_specialization1(jl_tupletype_t *types);
Expand Down
14 changes: 8 additions & 6 deletions src/table.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

static void **jl_table_lookup_bp(jl_array_t **pa, void *key);

void jl_idtable_rehash(jl_array_t **pa, size_t newsz)
JL_DLLEXPORT jl_array_t *jl_idtable_rehash(jl_array_t *a, size_t newsz)
{
// Assume *pa don't need a write barrier
// pa doesn't have to be a GC slot but *pa needs to be rooted
size_t sz = jl_array_len(*pa);
size_t sz = jl_array_len(a);
size_t i;
void **ol = (void**)(*pa)->data;
void **ol = (void**)a->data;
jl_array_t *newa = jl_alloc_vec_any(newsz);
// keep the original array in the original slot since we need `ol`
// to be valid in the loop below.
Expand All @@ -25,16 +25,16 @@ void jl_idtable_rehash(jl_array_t **pa, size_t newsz)
if (ol[i+1] != NULL) {
(*jl_table_lookup_bp(&newa, ol[i])) = ol[i+1];
jl_gc_wb(newa, ol[i+1]);
// it is however necessary here because allocation
// it is however necessary here because allocation
// can (and will) occur in a recursive call inside table_lookup_bp
}
}
*pa = newa;
// we do not check the write barrier here
// because pa always points to a C stack location
// (see jl_eqtable_put and jl_finalize_deserializer)
// it should be changed if this assumption no longer holds
JL_GC_POP();
return newa;
}

static void **jl_table_lookup_bp(jl_array_t **pa, void *key)
Expand All @@ -44,6 +44,7 @@ static void **jl_table_lookup_bp(jl_array_t **pa, void *key)
jl_array_t *a = *pa;
size_t orig, index, iter;
size_t newsz, sz = hash_size(a);
assert(sz >= 1);
size_t maxprobe = max_probe(sz);
void **tab = (void**)a->data;

Expand Down Expand Up @@ -81,7 +82,7 @@ static void **jl_table_lookup_bp(jl_array_t **pa, void *key)
newsz = HT_N_INLINE;
else
newsz = sz<<2;
jl_idtable_rehash(pa, newsz);
*pa = jl_idtable_rehash(*pa, newsz);

a = *pa;
tab = (void**)a->data;
Expand All @@ -98,6 +99,7 @@ static void **jl_table_lookup_bp(jl_array_t **pa, void *key)
static void **jl_table_peek_bp(jl_array_t *a, void *key)
{
size_t sz = hash_size(a);
assert(sz >= 1);
size_t maxprobe = max_probe(sz);
void **tab = (void**)a->data;
uint_t hv = keyhash((jl_value_t*)key);
Expand Down

0 comments on commit 032db5f

Please sign in to comment.