Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lua-style Dictionary operations are slower #68834

Closed
KoBeWi opened this issue Nov 18, 2022 · 26 comments · Fixed by #70096
Closed

Lua-style Dictionary operations are slower #68834

KoBeWi opened this issue Nov 18, 2022 · 26 comments · Fixed by #70096

Comments

@KoBeWi
Copy link
Member

KoBeWi commented Nov 18, 2022

Godot version

4.0 e8f9cd8

System information

Windows 10 x64

Issue description

Some facts:

  • Dictionary will implicitly convert StringName to Strings when used as keys
  • Lua-style syntax uses StringNames for access

As a result, Lua-style operations are about twice+ as slow. Benchmark I used:

var d: Dictionary
var time: int

time = Time.get_ticks_usec()
for i in 1000000:
	d.test1 = 1
	d.test2 = 2
	d.test3 = 4
	d.test4 = 8
print("Lua assign: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

time = Time.get_ticks_usec()
for i in 1000000:
	d["test1"] = 1
	d["test2"] = 2
	d["test3"] = 4
	d["test4"] = 8
print("Python assign: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

d = {test1 = 1, test2 = 2, test3 = 4, test4 = 8}
time = Time.get_ticks_usec()
for i in 1000000:
	var a = d.test1
	var b = d.test2
	var c = d.test3
	var e = d.test4
print("Lua read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

time = Time.get_ticks_usec()
for i in 1000000:
	var a = d["test1"]
	var b = d["test2"]
	var c = d["test3"]
	var e = d["test4"]
print("Python read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

My results:

Lua assign: 1746.421ms
Python assign: 693.359ms
Lua read: 1985.207ms
Python read: 862.008ms

I think Lua-style dicts should use Strings, to avoid useless conversions.

Steps to reproduce

See above.

Minimal reproduction project

N/A

@anvilfolk
Copy link
Contributor

anvilfolk commented Nov 18, 2022

Working on this one :) Think I have one possible cause of the issue narrowed down.

The lua style is using a subscript that has is_attribute set to true, whereas the python style is also using the same thing, but is_attribute is false. I'm going to try to reduce the first case to the second when the base is known to be a dictionary and the attribute can't be found in the builtin methods/properties.

@anvilfolk
Copy link
Contributor

anvilfolk commented Nov 20, 2022

I've tun into some memory issues, but wanted to leave this little exciting bit of progress :)

Untitled

@rune-scape
Copy link
Contributor

rune-scape commented Dec 4, 2022

with #68747 i got these numbers (i added a StringName benchmark):

Lua assign: 601.067ms
Python assign: 534.309ms
StringName assign: 649.116ms
Lua read: 939.54ms
Python read: 783.234ms
StringName read: 941.516ms

the only way i can think to make it faster would be to actually store StringName keys in Dictionaries instead of converting them (or convert attributes to Strings specifically for Dictionaries, but that seems hacky to me)

it doesn't completely fix this though, because it's still slower
i would have removed the StringName to String conversion in Dictionaries, but there was 1 piece of code that failed when i tried, and i was worried there might have been more i couldn't find

@KoBeWi
Copy link
Member Author

KoBeWi commented Dec 4, 2022

So I got a weird idea. Right now most of the Dictionary code looks like this:

if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key);
return _p->variant_map[sn->operator String()];
} else {
return _p->variant_map[p_key];
}

What if it instead was

if (p_key.get_type() == Variant::STRING_NAME && !_p->variant_map.has(p_key)) {
	const StringName *sn = VariantInternal::get_string_name(&p_key);
	return _p->variant_map[sn->operator String()];
} else {
	return _p->variant_map[p_key];
}

(note the has() check)?

Implicit Strings would become a fallback rather than being forced. Assuming that checking for some value is faster than casting to String, this could improve performance. The problem is that it needs to work 2-way, i.e. Strings should work with StringName keys. Also I wasn't able to figure out how to allow assigning StringName keys, because Dictionary seems to use [] operator for both reading and writing.

@anvilfolk
Copy link
Contributor

anvilfolk commented Dec 4, 2022

The solution I was working on does something different. In the analyzer, it checks subscripts base.prop, and if base is a dictionary, and prop is not a function, then it converts the IdentifierNode prop which uses a StringName into a LiteralNode which uses a String.

Since this is done at compile (analyze) time, it basically makes reduces the runtime performance of dict.prop into the runtime performance of dict["prop].

master...anvilfolk:godot:lua

It does, however, assume that Dictionary has no builtin properties, though I'm pretty sure we can do a ClassDB check for whether the property exists?

Perhaps it could be used in addition to this @rune-scape's PR, though it is a very local solution to this problem.

@rune-scape
Copy link
Contributor

rune-scape commented Dec 4, 2022

@KoBeWi that would add another check to every non-const StringName access, i guess compiler Are magic, so maybe it would get folded down? 🤷
and yea, the [] operator is the only way to insert a key into a dictionary afaik
the 2-way stuff is solved by changing the hashmap comparator, which is how i got the speed increase
again i don't think theres actually any way of making StringName accessing a String key entry in a dictionary faster than #68747, unless StringName keys are allowed, or what @anvilfolk suggested gets put in

@anvilfolk
Copy link
Contributor

anvilfolk commented Dec 4, 2022

I wouldn't be against simply removing dict.prop entirely from GDScript for accessing dictionary entries from GDScript.

Although, perhaps for dictionaries whose keys are meant to be StringNames, people might get some performance benefits?

Something like

dict[&"key"] = "val"
print(dict.key)

Would this be faster?

@akien-mga akien-mga added this to the 4.0 milestone Dec 9, 2022
@akien-mga
Copy link
Member

Should be fixed by #68747.

@KoBeWi
Copy link
Member Author

KoBeWi commented Dec 9, 2022

I tested and no, it wasn't fixed. This code is responsible for slowdowns:

Variant &Dictionary::operator[](const Variant &p_key) {
if (unlikely(_p->read_only)) {
if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key);
*_p->read_only = _p->variant_map[sn->operator String()];
} else {
*_p->read_only = _p->variant_map[p_key];
}
return *_p->read_only;
} else {
if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key);
return _p->variant_map[sn->operator String()];
} else {
return _p->variant_map[p_key];
}
}
}

It was not modified by that PR.

@KoBeWi KoBeWi reopened this Dec 9, 2022
@anvilfolk
Copy link
Contributor

Decided to reopen #68925 in an attempt to address this issue. I feel like that approach might make more sense since it is static/compile time checks rather than every time you try to access/modify a dictionary.

That said, it does use the analyzer to change the code tree generated by the parser, so I will see if I can implement @adamscott 's suggestion and try to move it to the parser. Hopefully that is possible!

@rune-scape
Copy link
Contributor

rune-scape commented Dec 9, 2022

@KoBeWi you're right it wasn't fixed, but lua-style reading of a dict entry should be faster, since it no longer needs to convert StringNames to String for every single dictionary operation, just the ones that insert (dict.key = value)
this means that it can use the cached hash of StringName then convert only when it needs to compare equality
i'll be able to actually test the current master branch after it compiles

@anvilfolk
Copy link
Contributor

anvilfolk commented Dec 9, 2022

Some more tests ran. Here's the code:

@tool
extends Node
func _process(delta):
	if Input.is_action_just_pressed("ui_cancel"):
		var d: Dictionary
		var time: int

		time = Time.get_ticks_usec()
		for i in 1000000:
			d.test1 = 1
			d.test2 = 2
			d.test3 = 4
			d.test4 = 8
		print("Lua assign: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

		time = Time.get_ticks_usec()
		for i in 1000000:
			d["test1"] = 1
			d["test2"] = 2
			d["test3"] = 4
			d["test4"] = 8
		print("Python assign: ", (Time.get_ticks_usec() - time) * 0.001, "ms")
		
		time = Time.get_ticks_usec()
		for i in 1000000:
			d[&"test1"] = 1
			d[&"test2"] = 2
			d[&"test3"] = 4
			d[&"test4"] = 8
		print("Python assign StringName: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

		d = {test1 = 1, test2 = 2, test3 = 4, test4 = 8}
		time = Time.get_ticks_usec()
		for i in 1000000:
			var a = d.test1
			var b = d.test2
			var c = d.test3
			var e = d.test4
		print("StringName dict lua read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

		time = Time.get_ticks_usec()
		for i in 1000000:
			var a = d["test1"]
			var b = d["test2"]
			var c = d["test3"]
			var e = d["test4"]
		print("StringName dict python String read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

		time = Time.get_ticks_usec()
		for i in 1000000:
			var a = d[&"test1"]
			var b = d[&"test2"]
			var c = d[&"test3"]
			var e = d[&"test4"]
		print("StringName dict python StringName read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")
		
		
		d = {"test1" = 1, "test2" = 2, "test3" = 4, "test4" = 8}
		time = Time.get_ticks_usec()
		for i in 1000000:
			var a = d.test1
			var b = d.test2
			var c = d.test3
			var e = d.test4
		print("String dict lua read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

		time = Time.get_ticks_usec()
		for i in 1000000:
			var a = d["test1"]
			var b = d["test2"]
			var c = d["test3"]
			var e = d["test4"]
		print("String dict python String read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

		time = Time.get_ticks_usec()
		for i in 1000000:
			var a = d[&"test1"]
			var b = d[&"test2"]
			var c = d[&"test3"]
			var e = d[&"test4"]
		print("String dict python StringName read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

On master:

Lua assign: 1265.423ms
Python assign: 598.055ms
Python assign StringName: 1392.699ms
StringName dict lua read: 1559.54ms
StringName dict python String read: 913.335ms
StringName dict python StringName read: 1565.882ms
String dict lua read: 1558.73ms
String dict python String read: 921.218ms
String dict python StringName read: 1586.356ms

On #68925:

Lua assign: 626.449ms
Python assign: 599.093ms
Python assign StringName: 1458.387ms
StringName dict lua read: 885.596ms
StringName dict python String read: 889.77ms
StringName dict python StringName read: 1583.906ms
String dict lua read: 900.751ms
String dict python String read: 896.726ms
String dict python StringName read: 1588.175ms

@anvilfolk
Copy link
Contributor

anvilfolk commented Dec 9, 2022

So now it feels like that's a general improvement across the board, but I'm not certain it really should be. If the keys of a dictionary are StringName, then shouldn't StringName access be really fast?

Looks like performance is subpar in that situation in both branches:

StringName dict python StringName read: 1565.882ms

I would've expected this to be faster on master and slower on 68925 due to StringName to String conversion, though I think that direction is much faster than the other way around?

I haven't fooled around too deep into this, so I'm just trying to provide more data. Let me know if there's anything I can do to help!

@rune-scape
Copy link
Contributor

rune-scape commented Dec 9, 2022

thanks for the numbers! huh
dictionaries currently convert all StringName keys to String before inserting, making all stringname access slower

@anvilfolk
Copy link
Contributor

I see! That makes more sense!

Might there be a way to have StringName keys for dictionaries? Maybe dict.allow/force_string_name_keys(true), or something. I can imagine that in some performance-critical situations, devs might opt to use dictionaries to store well-known properties indexed with StringNames.

I wonder how much more performance you'd get out of that compared to String indexing.

@rune-scape
Copy link
Contributor

rune-scape commented Dec 9, 2022

@anvilfolk weird, i got completely different results:
on 95a85c9:

Lua assign: 342.378ms
Python assign: 304.061ms
Python assign StringName: 334.535ms
StringName dict lua read: 421.319ms
StringName dict python String read: 315.74ms
StringName dict python StringName read: 402.837ms
String dict lua read: 404.339ms
String dict python String read: 320.014ms
String dict python StringName read: 402.221ms

on ae86d90 (after #68747 was merged):

Lua assign: 331.246ms
Python assign: 307.659ms
Python assign StringName: 349.445ms
StringName dict lua read: 396.99ms
StringName dict python String read: 337.034ms
StringName dict python StringName read: 389.819ms
String dict lua read: 388.097ms
String dict python String read: 321.843ms
String dict python StringName read: 381.605ms

both are production builds compiled on my machine

@anvilfolk
Copy link
Contributor

anvilfolk commented Dec 9, 2022

Decided to check on release_template as well to make sure results were similar to debug or editor builds or not. It's so much faster, and you're right, the difference between lua & python assigns is much smaller as well. Would you be able run it inside the editor? The only thing I can imagine is that it's something related to editor checks with TOOLS_ENABLED?

I compiled this right after your PR's merge, I'm on 907298d and rebased my PR on top of that.

Master:

Lua assign: 170.021ms
Python assign: 145.009ms
Python assign StringName: 194.028ms
StringName dict lua read: 225.148ms
StringName dict python String read: 153.31ms
StringName dict python StringName read: 214.007ms
String dict lua read: 212.727ms
String dict python String read: 147.148ms
String dict python StringName read: 218.998ms

Lua-access PR:

Lua assign: 142.292ms
Python assign: 148.14ms
Python assign StringName: 190.986ms
StringName dict lua read: 145.587ms
StringName dict python String read: 145.705ms
StringName dict python StringName read: 196.824ms
String dict lua read: 149.359ms
String dict python String read: 144.216ms
String dict python StringName read: 197.625ms

@anvilfolk
Copy link
Contributor

I'm going to update to the latest master as of right now, compile editor with dev_mode and dev_build, compile template_release and template_debug and get us some more data just in case. At the very least we'll figure out whether it's DEBUG_ENABLED or TOOLS_ENABLED causing that big change.

@rune-scape
Copy link
Contributor

rune-scape commented Dec 9, 2022

if i just take out the StringName to String conversion i get these numbers (again, production build):

Lua assign: 2556.085ms
Python assign: 2899.335ms
Python assign StringName: 2234.144ms
StringName dict lua read: 3013.939ms
StringName dict python String read: 3693.017ms
StringName dict python StringName read: 3035.785ms
String dict lua read: 3737.402ms
String dict python String read: 3321.441ms
String dict python StringName read: 3800.949ms

it certainly makes StringName faster
Edit: @anvilfolk ur test uses lua syntax for the string dict, i think you meant to use python syntax

@anvilfolk
Copy link
Contributor

anvilfolk commented Dec 10, 2022

Ok, I'm a nerd and for some reason and having fun with this. Here's the data I got in spreadsheet format.

Raw data:

image

Just master runtime for debug and release:

image

Debug and release for both master and my PR:

image

All that plus in-editor runs:

image

@anvilfolk
Copy link
Contributor

Those weren't actually very helpful at all. Except to notice that editor made everything worse?

master results for a release build.
image

And comparison with my PR:
image

@anvilfolk
Copy link
Contributor

anvilfolk commented Dec 10, 2022

So here's my takeaway right now. Sorry to spam you all, but I'm enjoying digging into this one. Feel free to take your time to read (or just ignore!). TL;DR at the end.

Currently, String-based accesses are 25% faster than StringName-based accesses.

Across the board, if your method for accessing a dictionary uses a String, then it's the fastest it can be on the master build. Any access which uses StringNames, such as doing dict.this_is_a_stringname or dict[&"this_is_a_stringname"], has a performance penalty to the tune of about 1/3.

That difference in-editor is actually closer to 40-50% faster for String-based methods

For whatever reason, being in editor (I suspect just meaning TOOLS_ENABLED) significantly exacerbates the difference between String- vs StringName-indexed methods. No idea why that would be, but the big difference doesn't appear between release and debug builds, so it has to be the editor.

StringName-based dicts could be up 10% faster than String-based ones

According to @rune-scape's test of performance before and after the merge for unifying String and StringName:

StringName dict lua read: 3013.939ms
String dict python String read: 3321.441ms

This means that fully StringName-based dicts (with StringName-based accesses) could be significantly faster in the grand scheme of things. Right now this isn't possible since all dictionaries are implicitly converted to being String-based only.

If sticking to String-based dictionaries, #68925 fixes lua-based access underperformance by ~25%

Lua indexing like dict.my_prop is effectively forced to use StringName. In String-based dictionaries, that requires conversion from StringName to String, which has some cost.

TL;DR (aka my conclusion): having a StringName-based dictionary is worth it. If this is done, then #68925 should probably not be merged.

If we were to have something like my_dict.allow_string_name_keys(true), then I believe we would get the fastest possible results. For performance-critical parts of the code, or data-driven game architectures, StringName based access could provide a 10% increase in performance, which is significant.

If this is something that we want to have in godot, then forcibly reducing lua-based StringName accesses to String-based accesses, like what #68925 is doing, is probably a terrible idea :)

@Mickeon
Copy link
Contributor

Mickeon commented Dec 10, 2022

That's not to mention that keys of a bunch of nested String-only Dictionaries are likely to be the same all around (basically similarly to a JSON structure), which may potentially save a good chunk of memory.

@KoBeWi
Copy link
Member Author

KoBeWi commented Dec 10, 2022

If we were to have something like my_dict.allow_string_name_keys(true), then I believe we would get the fastest possible results.

I wonder if it could be a project setting 🤔

@rune-scape
Copy link
Contributor

thank you for the graphs @anvilfolk! they make my autism happy :)
but to be clear, the String conversion happens at a low level in c++, so a runtime switch wouldn't really make sense

@YuriSizov YuriSizov modified the milestones: 4.0, 4.1 Feb 27, 2023
@YuriSizov YuriSizov modified the milestones: 4.1, 4.2 Jun 22, 2023
@L4Vo5
Copy link
Contributor

L4Vo5 commented Aug 4, 2023

Silly example, but it's worth noting that StringName access is for some reason faster when the keys are long enough (int performance added for reference):

	var d: Dictionary
	var time: int
	# read performance was affected by the variable declarations
	var a: int
	var b: int
	var c: int
	var e: int

	time = Time.get_ticks_usec()
	for i in 1000000:
		d.test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 1
		d.test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 2
		d.test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 4
		d.test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 8
	print("Lua assign: ", (Time.get_ticks_usec() - time) * 0.001, "ms")
	time = Time.get_ticks_usec()
	for i in 1000000:
		d["test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 1
		d["test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 2
		d["test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 4
		d["test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 8
	print("Python assign: ", (Time.get_ticks_usec() - time) * 0.001, "ms")
	
	time = Time.get_ticks_usec()
	for i in 1000000:
		d[&"test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 1
		d[&"test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 2
		d[&"test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 4
		d[&"test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] = 8
	print("Python assign StringName: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

	d = {test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 1, test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 2, test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 4, test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 8}
	time = Time.get_ticks_usec()
	for i in 1000000:
		a = d.test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		b = d.test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		c = d.test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		e = d.test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	print("StringName dict lua read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

	time = Time.get_ticks_usec()
	for i in 1000000:
		a = d["test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		b = d["test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		c = d["test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		e = d["test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
	print("StringName dict python String read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

	time = Time.get_ticks_usec()
	for i in 1000000:
		a = d[&"test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		b = d[&"test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		c = d[&"test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		e = d[&"test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
	print("StringName dict python StringName read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")
	
	
	d = {"test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" = 1, "test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" = 2, "test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" = 4, "test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" = 8}
	time = Time.get_ticks_usec()
	for i in 1000000:
		a = d.test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		b = d.test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		c = d.test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		e = d.test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	print("String dict lua read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

	time = Time.get_ticks_usec()
	for i in 1000000:
		a = d["test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		b = d["test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		c = d["test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		e = d["test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
	print("String dict python String read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

	time = Time.get_ticks_usec()
	for i in 1000000:
		a = d[&"test1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		b = d[&"test2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		c = d[&"test3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
		e = d[&"test4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
	print("String dict python StringName read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")
	d = {}
	
	time = Time.get_ticks_usec()
	for i in 1000000:
		d[0] = 0
		d[1] = 1
		d[2] = 2
		d[3] = 3
	print("python int assign: ", (Time.get_ticks_usec() - time) * 0.001, "ms")
	time = Time.get_ticks_usec()
	for i in 1000000:
		a = d[0]
		b = d[1]
		c = d[2]
		e = d[3]
	print("python int read: ", (Time.get_ticks_usec() - time) * 0.001, "ms")

gives me this on 4.2 dev2 (running from the editor):

Lua assign: 680.009ms
Python assign: 1247.879ms
Python assign StringName: 1314.539ms
StringName dict lua read: 763.562ms
StringName dict python String read: 1351.039ms
StringName dict python StringName read: 772.735ms
String dict lua read: 755.639ms
String dict python String read: 1314.273ms
String dict python StringName read: 756.75ms
python int assign: 136.481ms
python int read: 181.979ms

"Python assign StringName" being a weird outlier there.

While in the shorter-named version, Strings are of course faster as established:

Lua assign: 247.813ms
Python assign: 193.062ms
Python assign StringName: 260.788ms
StringName dict lua read: 340.491ms
StringName dict python String read: 258.217ms
StringName dict python StringName read: 354.51ms
String dict lua read: 351.861ms
String dict python String read: 248.908ms
String dict python StringName read: 338.807ms
python int assign: 138.84ms
python int read: 180.552ms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment