map: optimize free() and keys() (#7152)

pull/7153/head
ka-weihe 2020-12-06 00:24:24 +01:00 committed by GitHub
parent 8adb1acf31
commit 5a7fdb0610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 83 deletions

View File

@ -3,7 +3,7 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module builtin module builtin
//import hash.wyhash as hash // import hash.wyhash as hash
import hash import hash
/* /*
@ -56,7 +56,6 @@ find the index for their meta's in the new array. Instead of rehashing compl-
etely, it simply uses the cached-hashbits stored in the meta, resulting in etely, it simply uses the cached-hashbits stored in the meta, resulting in
much faster rehashing. much faster rehashing.
*/ */
const ( const (
// Number of bits from the hash stored for each entry // Number of bits from the hash stored for each entry
hashbits = 24 hashbits = 24
@ -224,30 +223,28 @@ fn new_map_1(value_bytes int) map {
fn new_map_init(n int, value_bytes int, keys &string, values voidptr) map { fn new_map_init(n int, value_bytes int, keys &string, values voidptr) map {
mut out := new_map_1(value_bytes) mut out := new_map_1(value_bytes)
for i in 0 .. n { for i in 0 .. n {
unsafe { unsafe {out.set(keys[i], byteptr(values) + i * value_bytes)}
out.set(keys[i], byteptr(values) + i * value_bytes)
}
} }
return out return out
} }
[inline] [inline]
fn (m &map) key_to_index(key string) (u32,u32) { fn (m &map) key_to_index(key string) (u32, u32) {
hash := hash.wyhash_c(key.str, u64(key.len), 0) hash := hash.wyhash_c(key.str, u64(key.len), 0)
index := hash & m.cap index := hash & m.cap
meta := ((hash >> m.shift) & hash_mask) | probe_inc meta := ((hash >> m.shift) & hash_mask) | probe_inc
return u32(index),u32(meta) return u32(index), u32(meta)
} }
[inline] [inline]
fn (m &map) meta_less(_index u32, _metas u32) (u32,u32) { fn (m &map) meta_less(_index u32, _metas u32) (u32, u32) {
mut index := _index mut index := _index
mut meta := _metas mut meta := _metas
for meta < unsafe {m.metas[index]} { for meta < unsafe {m.metas[index]} {
index += 2 index += 2
meta += probe_inc meta += probe_inc
} }
return index,meta return index, meta
} }
[inline] [inline]
@ -261,13 +258,11 @@ fn (mut m map) meta_greater(_index u32, _metas u32, kvi u32) {
tmp_meta := m.metas[index] tmp_meta := m.metas[index]
m.metas[index] = meta m.metas[index] = meta
meta = tmp_meta meta = tmp_meta
} tmp_index := m.metas[index + 1]
tmp_index := unsafe {m.metas[index + 1]}
unsafe {
m.metas[index + 1] = kv_index m.metas[index + 1] = kv_index
}
kv_index = tmp_index kv_index = tmp_index
} }
}
index += 2 index += 2
meta += probe_inc meta += probe_inc
} }
@ -305,8 +300,8 @@ fn (mut m map) set(k string, value voidptr) {
if load_factor > max_load_factor { if load_factor > max_load_factor {
m.expand() m.expand()
} }
mut index,mut meta := m.key_to_index(key) mut index, mut meta := m.key_to_index(key)
index,meta = m.meta_less(index, meta) index, meta = m.meta_less(index, meta)
// While we might have a match // While we might have a match
for meta == unsafe {m.metas[index]} { for meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]}) kv_index := int(unsafe {m.metas[index + 1]})
@ -335,8 +330,7 @@ fn (mut m map) expand() {
m.shift += max_cached_hashbits m.shift += max_cached_hashbits
m.cached_hashbits = max_cached_hashbits m.cached_hashbits = max_cached_hashbits
m.rehash() m.rehash()
} } else {
else {
m.cached_rehash(old_cap) m.cached_rehash(old_cap)
m.cached_hashbits-- m.cached_hashbits--
} }
@ -359,8 +353,8 @@ fn (mut m map) rehash() {
continue continue
} }
pkey := unsafe {&string(m.key_values.key(i))} pkey := unsafe {&string(m.key_values.key(i))}
mut index,mut meta := m.key_to_index(*pkey) mut index, mut meta := m.key_to_index(*pkey)
index,meta = m.meta_less(index, meta) index, meta = m.meta_less(index, meta)
m.meta_greater(index, meta, u32(i)) m.meta_greater(index, meta, u32(i))
} }
} }
@ -381,13 +375,11 @@ fn (mut m map) cached_rehash(old_cap u32) {
old_index := (i - old_probe_count) & (m.cap >> 1) old_index := (i - old_probe_count) & (m.cap >> 1)
mut index := (old_index | (old_meta << m.shift)) & m.cap mut index := (old_index | (old_meta << m.shift)) & m.cap
mut meta := (old_meta & hash_mask) | probe_inc mut meta := (old_meta & hash_mask) | probe_inc
index,meta = m.meta_less(index, meta) index, meta = m.meta_less(index, meta)
kv_index := unsafe {old_metas[i + 1]} kv_index := unsafe {old_metas[i + 1]}
m.meta_greater(index, meta, kv_index) m.meta_greater(index, meta, kv_index)
} }
unsafe{ unsafe {free(old_metas)}
free(old_metas)
}
} }
// This method is used for assignment operators. If the argument-key // This method is used for assignment operators. If the argument-key
@ -395,7 +387,7 @@ fn (mut m map) cached_rehash(old_cap u32) {
// If the key exists, its respective value is returned. // If the key exists, its respective value is returned.
fn (mut m map) get_and_set(key string, zero voidptr) voidptr { fn (mut m map) get_and_set(key string, zero voidptr) voidptr {
for { for {
mut index,mut meta := m.key_to_index(key) mut index, mut meta := m.key_to_index(key)
for { for {
if meta == unsafe {m.metas[index]} { if meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]}) kv_index := int(unsafe {m.metas[index + 1]})
@ -406,7 +398,9 @@ fn (mut m map) get_and_set(key string, zero voidptr) voidptr {
} }
index += 2 index += 2
meta += probe_inc meta += probe_inc
if meta > unsafe {m.metas[index]} { break } if meta > unsafe {m.metas[index]} {
break
}
} }
// Key not found, insert key with zero-value // Key not found, insert key with zero-value
m.set(key, zero) m.set(key, zero)
@ -419,7 +413,7 @@ fn (mut m map) get_and_set(key string, zero voidptr) voidptr {
// the method returns a reference to its mapped value. // the method returns a reference to its mapped value.
// If not, a zero/default value is returned. // If not, a zero/default value is returned.
fn (m map) get(key string, zero voidptr) voidptr { fn (m map) get(key string, zero voidptr) voidptr {
mut index,mut meta := m.key_to_index(key) mut index, mut meta := m.key_to_index(key)
for { for {
if meta == unsafe {m.metas[index]} { if meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]}) kv_index := int(unsafe {m.metas[index + 1]})
@ -430,14 +424,16 @@ fn (m map) get(key string, zero voidptr) voidptr {
} }
index += 2 index += 2
meta += probe_inc meta += probe_inc
if meta > unsafe {m.metas[index]} { break } if meta > unsafe {m.metas[index]} {
break
}
} }
return zero return zero
} }
// Checks whether a particular key exists in the map. // Checks whether a particular key exists in the map.
fn (m map) exists(key string) bool { fn (m map) exists(key string) bool {
mut index,mut meta := m.key_to_index(key) mut index, mut meta := m.key_to_index(key)
for { for {
if meta == unsafe {m.metas[index]} { if meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]}) kv_index := int(unsafe {m.metas[index + 1]})
@ -448,15 +444,17 @@ fn (m map) exists(key string) bool {
} }
index += 2 index += 2
meta += probe_inc meta += probe_inc
if meta > unsafe {m.metas[index]} { break } if meta > unsafe {m.metas[index]} {
break
}
} }
return false return false
} }
// Removes the mapping of a particular key from the map. // Removes the mapping of a particular key from the map.
pub fn (mut m map) delete(key string) { pub fn (mut m map) delete(key string) {
mut index,mut meta := m.key_to_index(key) mut index, mut meta := m.key_to_index(key)
index,meta = m.meta_less(index, meta) index, meta = m.meta_less(index, meta)
// Perform backwards shifting // Perform backwards shifting
for meta == unsafe {m.metas[index]} { for meta == unsafe {m.metas[index]} {
kv_index := int(unsafe {m.metas[index + 1]}) kv_index := int(unsafe {m.metas[index + 1]})
@ -496,10 +494,17 @@ pub fn (mut m map) delete(key string) {
} }
// Returns all keys in the map. // Returns all keys in the map.
// TODO: add optimization in case of no deletes
pub fn (m &map) keys() []string { pub fn (m &map) keys() []string {
mut keys := []string{ len:m.len } mut keys := []string{len: m.len}
mut j := 0 mut j := 0
if m.key_values.deletes == 0 {
for i := 0; i < m.key_values.len; i++ {
pkey := unsafe {&string(m.key_values.key(i))}
keys[j] = pkey.clone()
j++
}
return keys
}
for i := 0; i < m.key_values.len; i++ { for i := 0; i < m.key_values.len; i++ {
if !m.key_values.has_index(i) { if !m.key_values.has_index(i) {
continue continue
@ -513,7 +518,7 @@ pub fn (m &map) keys() []string {
[unsafe] [unsafe]
pub fn (d DenseArray) clone() DenseArray { pub fn (d DenseArray) clone() DenseArray {
res := DenseArray { res := DenseArray{
key_bytes: d.key_bytes key_bytes: d.key_bytes
value_bytes: d.value_bytes value_bytes: d.value_bytes
slot_bytes: d.slot_bytes slot_bytes: d.slot_bytes
@ -539,17 +544,21 @@ pub fn (m map) clone() map {
extra_metas: m.extra_metas extra_metas: m.extra_metas
len: m.len len: m.len
} }
unsafe { unsafe {C.memcpy(res.metas, m.metas, metasize)}
C.memcpy(res.metas, m.metas, metasize)
}
return res return res
} }
[unsafe] [unsafe]
pub fn (m &map) free() { pub fn (m &map) free() {
unsafe {free(m.metas)}
if m.key_values.deletes == 0 {
for i := 0; i < m.key_values.len; i++ {
unsafe { unsafe {
free(m.metas) pkey := &string(m.key_values.key(i))
(*pkey).free()
} }
}
} else {
for i := 0; i < m.key_values.len; i++ { for i := 0; i < m.key_values.len; i++ {
if !m.key_values.has_index(i) { if !m.key_values.has_index(i) {
continue continue
@ -559,22 +568,6 @@ pub fn (m &map) free() {
(*pkey).free() (*pkey).free()
} }
} }
unsafe {
free(m.key_values.data)
} }
unsafe {free(m.key_values.data)}
} }
/*
pub fn (m map_string) str() string {
if m.len == 0 {
return '{}'
}
mut sb := strings.new_builder(50)
sb.writeln('{')
for key, val in m {
sb.writeln(' "$key" => "$val"')
}
sb.writeln('}')
return sb.str()
}
*/

View File

@ -1234,6 +1234,7 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
g.expr(it.cond) g.expr(it.cond)
g.writeln(';') g.writeln(';')
g.writeln('for (int $idx = 0; $idx < ${atmp}.key_values.len; ++$idx) {') g.writeln('for (int $idx = 0; $idx < ${atmp}.key_values.len; ++$idx) {')
// TODO: don't have this check when the map has no deleted elements
g.writeln('\tif (!DenseArray_has_index(&${atmp}.key_values, $idx)) {continue;}') g.writeln('\tif (!DenseArray_has_index(&${atmp}.key_values, $idx)) {continue;}')
if it.key_var != '_' { if it.key_var != '_' {
key_styp := g.typ(it.key_type) key_styp := g.typ(it.key_type)