Skip to content

Commit

Permalink
Save on memory more
Browse files Browse the repository at this point in the history
  • Loading branch information
Lyndon White committed Nov 24, 2017
1 parent dae2726 commit a5d65d4
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 38 deletions.
23 changes: 10 additions & 13 deletions src/corefunctionality.jl
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@

const pool = WeakKeyDict{String, WeakRef}()
const pool = WeakKeyDict{String, Void}()

# work around https://github.com/JuliaLang/julia/issues/24721
function patched_get!(wkd::WeakKeyDict{K}, key, default) where{K}
function intern!(wkd::WeakKeyDict{K}, key)::K where K
kk = convert(K, key)
kwr = WeakRef(kk)
lock(wkd) do
if haskey(wkd.ht, kwr)
return wkd.ht[kwr]
found_key = getkey(wkd.ht, kwr, Base.secret_table_token)
found = !(found_key === Base.secret_table_token)

if found
return found_key.value
else
# Not found, so add it,
# and mark it as a reference we track to delete!
finalizer(kk, wkd.finalizer)
return wkd.ht[kwr]=default
wkd.ht[kwr]=nothing
return kk
end
end
end

struct InternedString <: AbstractString
value::String

function InternedString(s)
#can't use get! as https://github.com/JuliaLang/julia/issues/24721
value = convert(String, s)
ret = new(patched_get!(pool, value, WeakRef(value)).value)
@assert ret.value == value
ret
end
InternedString(s) = new(intern!(pool, s))
end

macro i_str(s)
Expand Down
88 changes: 63 additions & 25 deletions test/corefunctionality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,68 @@ end end
end end


@testset "Garbage Collections" begin let
@testset "Garbage Collection 1" begin let
empty!(StringInterning.pool)
@test length(StringInterning.pool)==0
ai = InternedString("Hello My Friends3")
ai = [44] #remove the reference
gc();
@test 0<=length(StringInterning.pool)<=1 #May or may not have been collected yet
end end

@testset "Garbage Collection 2" begin let
empty!(StringInterning.pool)
@test length(StringInterning.pool)==0
ai = InternedString("Hello My Friends4")
bi = InternedString(join(["Hello", "My", "Friends4"], " "))
@test ai.value === bi.value
@test length(StringInterning.pool)==1
use(ai,bi)
ai = [44]
gc()
@test length(StringInterning.pool)==1 #don't collect when only one reference is gone
use(bi)
bi=[32]

@testset "Garbage Collection 1" begin let
empty!(StringInterning.pool)
@test length(StringInterning.pool)==0
ai = InternedString("Hello My Friends3")
ai = [44] #remove the reference
gc();
@test 0<=length(StringInterning.pool)<=1 #May or may not have been collected yet
end end

@testset "Garbage Collection 2" begin let
empty!(StringInterning.pool)
@test length(StringInterning.pool)==0
ai = InternedString("Hello My Friends4")
bi = InternedString(join(["Hello", "My", "Friends4"], " "))
@test ai.value === bi.value
@test length(StringInterning.pool)==1
use(ai,bi)
ai = [44]
gc()
@test length(StringInterning.pool)==1 #don't collect when only one reference is gone
use(bi)
bi=[32]
gc()
@test 0<=length(StringInterning.pool)<=1
end end



srand(1)
@testset "Garbage Collection stress test" begin let
empty!(StringInterning.pool)
oldpoolsize = length(StringInterning.pool)
function checkpool(op)
gc()
@test 0<=length(StringInterning.pool)<=1
end end
@test op(length(StringInterning.pool), oldpoolsize)
oldpoolsize = length(StringInterning.pool)
end

originals = [randstring(rand(1:1024)) for _ in 1:10^5]
n_orginals = length(originals)

interns = InternedString.(originals);
checkpool(>)

for ii in 1:10^5
push!(interns, InternedString(rand(originals)))
end
checkpool(==)
originals = nothing
checkpool(==)



for ii in 1:30
shuffle!(interns)
for jj in 1:1000
pop!(interns)
end
checkpool(<=)
end

# This one matters:
@test length(StringInterning.pool) < n_orginals
end end

0 comments on commit a5d65d4

Please sign in to comment.