map: optimize free() and keys() (#7152)
parent
8adb1acf31
commit
5a7fdb0610
|
@ -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
|
||||||
|
@ -98,10 +97,10 @@ struct DenseArray {
|
||||||
value_bytes int
|
value_bytes int
|
||||||
slot_bytes int // sum of 2 fields above
|
slot_bytes int // sum of 2 fields above
|
||||||
mut:
|
mut:
|
||||||
cap int
|
cap int
|
||||||
len int
|
len int
|
||||||
deletes u32 // count
|
deletes u32 // count
|
||||||
data byteptr // array of interspersed key data and value data
|
data byteptr // array of interspersed key data and value data
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
|
@ -204,7 +203,7 @@ mut:
|
||||||
extra_metas u32
|
extra_metas u32
|
||||||
pub mut:
|
pub mut:
|
||||||
// Number of key-values currently in the hashmap
|
// Number of key-values currently in the hashmap
|
||||||
len int
|
len int
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_map_1(value_bytes int) map {
|
fn new_map_1(value_bytes int) map {
|
||||||
|
@ -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,12 +258,10 @@ 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,14 +518,14 @@ 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
|
||||||
cap: d.cap
|
cap: d.cap
|
||||||
len: d.len
|
len: d.len
|
||||||
deletes: d.deletes
|
deletes: d.deletes
|
||||||
data: unsafe {memdup(d.data, d.cap * d.slot_bytes)}
|
data: unsafe {memdup(d.data, d.cap * d.slot_bytes)}
|
||||||
}
|
}
|
||||||
// FIXME clone each key
|
// FIXME clone each key
|
||||||
return res
|
return res
|
||||||
|
@ -530,51 +535,39 @@ pub fn (d DenseArray) clone() DenseArray {
|
||||||
pub fn (m map) clone() map {
|
pub fn (m map) clone() map {
|
||||||
metasize := int(sizeof(u32) * (m.cap + 2 + m.extra_metas))
|
metasize := int(sizeof(u32) * (m.cap + 2 + m.extra_metas))
|
||||||
res := map{
|
res := map{
|
||||||
value_bytes: m.value_bytes
|
value_bytes: m.value_bytes
|
||||||
cap: m.cap
|
cap: m.cap
|
||||||
cached_hashbits: m.cached_hashbits
|
cached_hashbits: m.cached_hashbits
|
||||||
shift: m.shift
|
shift: m.shift
|
||||||
key_values: unsafe {m.key_values.clone()}
|
key_values: unsafe {m.key_values.clone()}
|
||||||
metas: &u32(malloc(metasize))
|
metas: &u32(malloc(metasize))
|
||||||
extra_metas: m.extra_metas
|
extra_metas: m.extra_metas
|
||||||
len: m.len
|
len: m.len
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
C.memcpy(res.metas, m.metas, metasize)
|
|
||||||
}
|
}
|
||||||
|
unsafe {C.memcpy(res.metas, m.metas, metasize)}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
[unsafe]
|
[unsafe]
|
||||||
pub fn (m &map) free() {
|
pub fn (m &map) free() {
|
||||||
unsafe {
|
unsafe {free(m.metas)}
|
||||||
free(m.metas)
|
if m.key_values.deletes == 0 {
|
||||||
}
|
for i := 0; i < m.key_values.len; i++ {
|
||||||
for i := 0; i < m.key_values.len; i++ {
|
unsafe {
|
||||||
if !m.key_values.has_index(i) {
|
pkey := &string(m.key_values.key(i))
|
||||||
continue
|
(*pkey).free()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unsafe {
|
} else {
|
||||||
pkey := &string(m.key_values.key(i))
|
for i := 0; i < m.key_values.len; i++ {
|
||||||
(*pkey).free()
|
if !m.key_values.has_index(i) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
pkey := &string(m.key_values.key(i))
|
||||||
|
(*pkey).free()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {free(m.key_values.data)}
|
||||||
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()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue