Browse Source

[REFLECTION BREAKING] Modify the internals of the `map` type to increase performance

gingerBill 5 years ago
parent
commit
7f48cf8405
7 changed files with 108 additions and 97 deletions
  1. 1 5
      core/encoding/json/marshal.odin
  2. 2 2
      core/fmt/fmt.odin
  3. 44 16
      core/runtime/core.odin
  4. 2 0
      core/sys/windows/types.odin
  5. 2 0
      src/check_type.cpp
  6. 25 34
      src/ir.cpp
  7. 32 40
      src/llvm_backend.cpp

+ 1 - 5
core/encoding/json/marshal.odin

@@ -210,11 +210,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
 				data := uintptr(entries.data) + uintptr(i*entry_size);
 				header := cast(^Map_Entry_Header)data;
 
-				if reflect.is_string(info.key) {
-					marshal_arg(b, header.key.str);
-				} else {
-					marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
-				}
+				marshal_arg(b, any{rawptr(&header.key.key.val), info.key.id});
 
 				write_string(b, ": ");
 

+ 2 - 2
core/fmt/fmt.odin

@@ -1501,10 +1501,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 				header := cast(^runtime.Map_Entry_Header)data;
 
 				if reflect.is_string(info.key) {
-					strings.write_string(fi.buf, header.key.str);
+					strings.write_string(fi.buf, header.key.key.str);
 				} else {
 					fi := Info{buf = fi.buf};
-					fmt_arg(&fi, any{rawptr(&header.key.hash), info.key.id}, 'v');
+					fmt_arg(&fi, any{rawptr(&header.key.key.val), info.key.id}, 'v');
 				}
 
 				strings.write_string(fi.buf, "=");

+ 44 - 16
core/runtime/core.odin

@@ -349,7 +349,17 @@ INITIAL_MAP_CAP :: 16;
 
 Map_Key :: struct {
 	hash: u64,
-	str:  string,
+	/* NOTE(bill)
+		size_of(Map_Key) == 16 Bytes on 32-bit systems
+		size_of(Map_Key) == 24 Bytes on 64-bit systems
+
+		This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems
+		however, this is probably not a huge problem in terms of memory usage
+	*/
+	key: struct #raw_union {
+		str: string,
+		val: u64,
+	},
 }
 
 Map_Find_Result :: struct {
@@ -1407,28 +1417,34 @@ __get_map_key :: proc "contextless" (k: $K) -> Map_Key {
 	T :: intrinsics.type_core_type(K);
 
 	when intrinsics.type_is_integer(T) {
+		map_key.hash = default_hash_ptr(&key, size_of(T));
+
 		sz :: 8*size_of(T);
-		     when sz ==  8 do map_key.hash = u64(( ^u8)(&key)^);
-		else when sz == 16 do map_key.hash = u64((^u16)(&key)^);
-		else when sz == 32 do map_key.hash = u64((^u32)(&key)^);
-		else when sz == 64 do map_key.hash = u64((^u64)(&key)^);
-		else do #assert(false, "Unhandled integer size");
+		     when sz ==  8 do map_key.key.val = u64(( ^u8)(&key)^);
+		else when sz == 16 do map_key.key.val = u64((^u16)(&key)^);
+		else when sz == 32 do map_key.key.val = u64((^u32)(&key)^);
+		else when sz == 64 do map_key.key.val = u64((^u64)(&key)^);
+		else do #panic("Unhandled integer size");
 	} else when intrinsics.type_is_rune(T) {
-		map_key.hash = u64((^rune)(&key)^);
+		map_key.hash = default_hash_ptr(&key, size_of(T));
+		map_key.key.val = u64((^rune)(&key)^);
 	} else when intrinsics.type_is_pointer(T) {
-		map_key.hash = u64(uintptr((^rawptr)(&key)^));
+		map_key.hash = default_hash_ptr(&key, size_of(T));
+		map_key.key.val = u64(uintptr((^rawptr)(&key)^));
 	} else when intrinsics.type_is_float(T) {
+		map_key.hash = default_hash_ptr(&key, size_of(T));
+
 		sz :: 8*size_of(T);
-		     when sz == 32 do map_key.hash = u64((^u32)(&key)^);
-		else when sz == 64 do map_key.hash = u64((^u64)(&key)^);
-		else do #assert(false, "Unhandled float size");
+		     when sz == 32 do map_key.key.val = u64((^u32)(&key)^);
+		else when sz == 64 do map_key.key.val = u64((^u64)(&key)^);
+		else do #panic("Unhandled float size");
 	} else when intrinsics.type_is_string(T) {
 		#assert(T == string);
 		str := (^string)(&key)^;
 		map_key.hash = default_hash_string(str);
-		map_key.str  = str;
+		map_key.key.str = str;
 	} else {
-		#assert(false, "Unhandled map key type");
+		#panic("Unhandled map key type");
 	}
 
 	return map_key;
@@ -1443,10 +1459,18 @@ _fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) ->
 }
 
 
-default_hash :: proc "contextless" (data: []byte) -> u64 {
+default_hash :: inline proc "contextless" (data: []byte) -> u64 {
+	context = default_context();
+	os.write_string(os.stdout, "here - default_hash\n");
 	return _fnv64a(data);
 }
-default_hash_string :: proc "contextless" (s: string) -> u64 do return default_hash(transmute([]byte)(s));
+default_hash_string :: inline proc "contextless" (s: string) -> u64 {
+	return default_hash(transmute([]byte)(s));
+}
+default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 {
+	s := Raw_Slice{data, size};
+	return default_hash(transmute([]byte)(s));
+}
 
 
 source_code_location_hash :: proc(s: Source_Code_Location) -> u64 {
@@ -1582,7 +1606,11 @@ __dynamic_map_full :: inline proc(using h: Map_Header) -> bool {
 
 __dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool {
 	if a.hash == b.hash {
-		if h.is_key_string do return a.str == b.str;
+		if h.is_key_string {
+			return a.key.str == b.key.str;
+		} else {
+			return a.key.val == b.key.val;
+		}
 		return true;
 	}
 	return false;

+ 2 - 0
core/sys/windows/types.odin

@@ -17,6 +17,8 @@ HANDLE :: distinct LPVOID;
 HINSTANCE :: HANDLE;
 HMODULE :: distinct HINSTANCE;
 HRESULT :: distinct LONG;
+HWND :: distinct HANDLE;
+HMONITOR :: distinct HANDLE;
 BOOL :: distinct b32;
 BYTE :: distinct u8;
 BOOLEAN :: distinct b8;

+ 2 - 0
src/check_type.cpp

@@ -2794,6 +2794,8 @@ void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
 
 	if (is_type_string(key)) {
 		add_package_dependency(ctx, "runtime", "default_hash_string");
+	} else {
+		add_package_dependency(ctx, "runtime", "default_hash_ptr");
 	}
 
 

+ 25 - 34
src/ir.cpp

@@ -3593,27 +3593,8 @@ irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) {
 	irValue *v = ir_add_local_generated(proc, t_map_key, true);
 	Type *t = base_type(ir_type(key));
 	key = ir_emit_conv(proc, key, key_type);
-	if (is_type_integer(t)) {
-		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, key, hash_type));
-	} else if (is_type_enum(t)) {
-		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, key, hash_type));
-	} else if (is_type_typeid(t)) {
-		irValue *i = ir_emit_bitcast(proc, key, t_uint);
-		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, i, hash_type));
-	} else if (is_type_pointer(t)) {
-		irValue *p = ir_emit_conv(proc, key, t_uintptr);
-		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, p, hash_type));
-	} else if (is_type_float(t)) {
-		irValue *bits = nullptr;
-		i64 size = type_size_of(t);
-		switch (8*size) {
-		case 32:  bits = ir_emit_transmute(proc, key, t_u32); break;
-		case 64:  bits = ir_emit_transmute(proc, key, t_u64);  break;
-		default: GB_PANIC("Unhandled float size: %lld bits", size); break;
-		}
-
-		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), ir_emit_conv(proc, bits, hash_type));
-	} else if (is_type_string(t)) {
+
+	if (is_type_string(t)) {
 		irValue *str = ir_emit_conv(proc, key, t_string);
 		irValue *hashed_str = nullptr;
 
@@ -3628,9 +3609,27 @@ irValue *ir_gen_map_key(irProcedure *proc, irValue *key, Type *key_type) {
 			hashed_str = ir_emit_runtime_call(proc, "default_hash_string", args);
 		}
 		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 0), hashed_str);
-		ir_emit_store(proc, ir_emit_struct_ep(proc, v, 1), str);
+
+		irValue *key_data = ir_emit_struct_ep(proc, v, 1);
+		key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
+		ir_emit_store(proc, key_data, str);
 	} else {
-		GB_PANIC("Unhandled map key type");
+		i64 sz = type_size_of(t);
+		GB_ASSERT(sz <= 8);
+		if (sz != 0) {
+			auto args = array_make<irValue *>(ir_allocator(), 2);
+			args[0] = ir_address_from_load_or_generate_local(proc, key);
+			args[1] = ir_const_int(sz);
+			irValue *hash = ir_emit_runtime_call(proc, "default_hash_ptr", args);
+
+
+			irValue *hash_ptr = ir_emit_struct_ep(proc, v, 0);
+			irValue *key_data = ir_emit_struct_ep(proc, v, 1);
+			key_data = ir_emit_conv(proc, key_data, alloc_type_pointer(key_type));
+
+			ir_emit_store(proc, hash_ptr, hash);
+			ir_emit_store(proc, key_data, key);
+		}
 	}
 
 	return ir_emit_load(proc, v);
@@ -9818,8 +9817,6 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, ir
 		break;
 	}
 	case Type_Map: {
-		irValue *key = ir_add_local_generated(proc, expr_type->Map.key, true);
-
 		irValue *entries = ir_map_entries_ptr(proc, expr);
 		irValue *elem = ir_emit_struct_ep(proc, entries, 0);
 		elem = ir_emit_load(proc, elem);
@@ -9827,15 +9824,9 @@ void ir_build_range_indexed(irProcedure *proc, irValue *expr, Type *val_type, ir
 		irValue *entry = ir_emit_ptr_offset(proc, elem, idx);
 		val = ir_emit_load(proc, ir_emit_struct_ep(proc, entry, 2));
 
-		irValue *hash = ir_emit_struct_ep(proc, entry, 0);
-		if (is_type_string(expr_type->Map.key)) {
-			irValue *str = ir_emit_struct_ep(proc, hash, 1);
-			ir_emit_store(proc, key, ir_emit_load(proc, str));
-		} else {
-			irValue *hash_ptr = ir_emit_struct_ep(proc, hash, 0);
-			hash_ptr = ir_emit_conv(proc, hash_ptr, ir_type(key));
-			ir_emit_store(proc, key, ir_emit_load(proc, hash_ptr));
-		}
+		irValue *key_raw = ir_emit_struct_ep(proc, entry, 0);
+		key_raw = ir_emit_struct_ep(proc, key_raw, 1);
+		irValue *key = ir_emit_conv(proc, key_raw, alloc_type_pointer(expr_type->Map.key));
 
 		idx = ir_emit_load(proc, key);
 

+ 32 - 40
src/llvm_backend.cpp

@@ -3077,8 +3077,6 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
 		break;
 	}
 	case Type_Map: {
-		lbAddr key = lb_add_local_generated(p, expr_type->Map.key, true);
-
 		lbValue entries = lb_map_entries_ptr(p, expr);
 		lbValue elem = lb_emit_struct_ep(p, entries, 0);
 		elem = lb_emit_load(p, elem);
@@ -3086,17 +3084,11 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
 		lbValue entry = lb_emit_ptr_offset(p, elem, idx);
 		val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2));
 
-		lbValue hash = lb_emit_struct_ep(p, entry, 0);
-		if (is_type_string(expr_type->Map.key)) {
-			lbValue str = lb_emit_struct_ep(p, hash, 1);
-			lb_addr_store(p, key, lb_emit_load(p, str));
-		} else {
-			lbValue hash_ptr = lb_emit_struct_ep(p, hash, 0);
-			hash_ptr = lb_emit_conv(p, hash_ptr, key.addr.type);
-			lb_addr_store(p, key, lb_emit_load(p, hash_ptr));
-		}
+		lbValue key_raw = lb_emit_struct_ep(p, entry, 0);
+		key_raw = lb_emit_struct_ep(p, key_raw, 1);
+		lbValue key = lb_emit_conv(p, key_raw, alloc_type_pointer(expr_type->Map.key));
 
-		idx = lb_addr_load(p, key);
+		idx = lb_emit_load(p, key);
 
 		break;
 	}
@@ -9655,45 +9647,45 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) {
 lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) {
 	Type *hash_type = t_u64;
 	lbAddr v = lb_add_local_generated(p, t_map_key, true);
+	lbValue vp = lb_addr_get_ptr(p, v);
 	Type *t = base_type(key.type);
 	key = lb_emit_conv(p, key, key_type);
-	if (is_type_integer(t)) {
-		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, key, hash_type));
-	} else if (is_type_enum(t)) {
-		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, key, hash_type));
-	} else if (is_type_typeid(t)) {
-		lbValue i = lb_emit_transmute(p, key, t_uint);
-		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, i, hash_type));
-	} else if (is_type_pointer(t)) {
-		lbValue ptr = lb_emit_conv(p, key, t_uintptr);
-		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, ptr, hash_type));
-	} else if (is_type_float(t)) {
-		lbValue bits = {};
-		i64 size = type_size_of(t);
-		switch (8*size) {
-		case 32:  bits = lb_emit_transmute(p, key, t_u32); break;
-		case 64:  bits = lb_emit_transmute(p, key, t_u64);  break;
-		default: GB_PANIC("Unhandled float size: %lld bits", size); break;
-		}
-
-		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, bits, hash_type));
-	} else if (is_type_string(t)) {
+
+	if (is_type_string(t)) {
 		lbValue str = lb_emit_conv(p, key, t_string);
 		lbValue hashed_str = {};
 
-		if (false && lb_is_const(str)) {
-			String value = lb_get_const_string(p->module, str);
-			u64 hs = fnv64a(value.text, value.len);
-			hashed_str = lb_const_value(p->module, t_u64, exact_value_u64(hs));
+		if (lb_is_const(str)) {
+			String v = lb_get_const_string(p->module, str);
+			u64 hs = fnv64a(v.text, v.len);
+			hashed_str = lb_const_int(p->module, t_u64, hs);
 		} else {
 			auto args = array_make<lbValue>(heap_allocator(), 1);
 			args[0] = str;
 			hashed_str = lb_emit_runtime_call(p, "default_hash_string", args);
 		}
-		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), hashed_str);
-		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 1), str);
+		lb_emit_store(p, lb_emit_struct_ep(p, vp, 0), hashed_str);
+
+		lbValue key_data = lb_emit_struct_ep(p, vp, 1);
+		key_data = lb_emit_conv(p, key_data, alloc_type_pointer(key_type));
+		lb_emit_store(p, key_data, str);
 	} else {
-		GB_PANIC("Unhandled map key type");
+		i64 sz = type_size_of(t);
+		GB_ASSERT(sz <= 8);
+		if (sz != 0) {
+			auto args = array_make<lbValue>(heap_allocator(), 2);
+			args[0] = lb_address_from_load_or_generate_local(p, key);
+			args[1] = lb_const_int(p->module, t_int, sz);
+			lbValue hash = lb_emit_runtime_call(p, "default_hash_ptr", args);
+
+
+			lbValue hash_ptr = lb_emit_struct_ep(p, vp, 0);
+			lbValue key_data = lb_emit_struct_ep(p, vp, 1);
+			key_data = lb_emit_conv(p, key_data, alloc_type_pointer(key_type));
+
+			lb_emit_store(p, hash_ptr, hash);
+			lb_emit_store(p, key_data, key);
+		}
 	}
 
 	return lb_addr_load(p, v);