- Home /
Does Unity's Mono do a reference compare in its string == operator?
According to this page: http://www.dotnetperls.com/string-intern
"It is faster to compare interned strings because the method that C# implements string comparisons with always checks references first. If the references are equal, the comparison succeeds."
Internally, code supposedly looks like this:
public static bool Equals(string a, string b)
{
return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b)));
}
But this page is describing Microsoft's CLR.
Does the same behavior apply to Unity's version of Mono as well? Does the == operator between strings do a reference compare before the per-character string compare?
And if so, would GameObject tags be interned at runtime?
Apart from the lack of code completion (although Visual Studio now checks misspellings), I've always avoided using and creating string interfaces because of the performance implications. But, barring the lack of code completion, string interfaces are arguably more scaleable and easier to set up, versus setting up reference identification or new enums. Those are really good things to skip if you're still at prototyping stages.
But if string interning does what it does, we could just have a preparatory step for string member fields where applicable (calling myString = string.Intern(myString);
on initialization), and the resulting reference comparisons from == operators would theoretically make it fast enough even for performance-critical code.
More info: https://msdn.microsoft.com/en-us/library/system.string.intern%28v=vs.85%29.aspx
EDIT: I guess to clarify, the insight here was that interned strings might be used to identify certain kinds of data in a collection, for example, if you had a centralized place for playing UI sounds and you wanted to identify sounds by their name.
But I guess in actual use, getting data identified by a string through a string interface would involve either traversing a List and checking each item, or accessing it through a Dictionary.
The List solution is nicer in Unity because Lists serialize. And in this case, you'd still be traversing a List, but one could at least rest easy that you could reliably do reference comparisons object.ReferenceCompare(string1, string2);
if you interned your strings correctly.
I guess the == operator didn't really matter, 'cause if that fails, it would still attempt the char-by-char comparison.
It just wouldn't matter in the Dictionary case either since access is through hash values.
I guess this still has other uses somehow.
Answer by Bunny83 · Sep 01, 2015 at 01:20 PM
Yes, strings are compared by reference first. However i'm not entirely sure if tags are interned. You could simply test it by using IsInterned on the returned tag of a gameobject to see if it's an interned value. All literal string constants in your code are interned by default.
While it's true that an interned string comparison is quite a bit faster than a char by char comparison it most likely won't increase the performance. "char by char" comparison isn't that slow as it's just a for loop. Also it only has to check the full length of the string if they actually match. The first character that doesn't match yields an early exit.
So if you check:
if (gameObject.tag == "MyLongTagName")
if the actual tag is "Player1234567" it only compares one character ('M' and 'P') and returns false
if the actual tag is "MyLongTagName2" it usually should early exit as well since the length of both strings doesn't match.
Only in cases where the actual tag is something like "MyLongTagNamX" it would have to check all characters up to the X until it realises that they don't match.
So in reality it won't make much of a difference. It's much more important to avoid things like that:
int i = 1;
if (gameObject.tag == ("MyTag" + i))
The comparison here is quite irrelevant but that you create a new string each time you execute this line. Cache strings where possible if they are dynamically build.
edit
Just did a quick test and yes, tag returned an interned string if the string exists in the intern pool.
I simply did this on the MainCamera:
if (string.IsInterned(gameObject.tag) != null)
Debug.Log("Is interned");
If i just execute this it won't print. However if i have the string "MainCamera" somewhere in my project it will print.
edit2
Oh, i think i've misinterpreted what IsInterned actually does ^^. It doesn't check if the string is actually interned but if the string that you pass to the method exists in the intern table. It doesn't say anything about if the passed in string is actually interned...
So after a bit more testing it turned out that the string is not interned. If you do:
if ((object)gameObject.tag == (object)"MainCamera")
It will never return true.... Even "MainCamera" is an interned string, the string returned by "tag" is not interned.
So the final answer is: Interning a string for tag comparison doesn't help at all ^^. I guess that CompareTag might use an interned or look up table internally since it was always stated that it's faster than a normal string comparison.
Thanks! Those are all great insights. And thanks for investigating it thoroughly too.
I wasn't planning to use it for tags in particular, but your insight on that was helpful too.
I was thinking of using a string as an identifier for items in a collection. I guess that's technically what Dictionaries are for, and string interning doesn't even factor into that because it just uses hashcodes.
I'm sure there's an appropriate use for this somewhere. XD
Do you think a Dictionary is abusive? XD I could access stuff from it by passing it a (object)myInternedString
so it does a reference hashcode ins$$anonymous$$d of a string hashcode. hahaha
Answer by steakpinball · Sep 01, 2015 at 01:45 PM
This does apply to unity's implementation. You can view their implementation on Github. https://github.com/Unity-Technologies/mono/blob/unity-staging/mcs/class/corlib/System/String.cs
public static bool operator == (String a, String b)
{
return Equals (a, b);
}
public static unsafe bool Equals (string a, string b)
{
if ((a as object) == (b as object))
return true;
if (a == null || b == null)
return false;
...