Skip to content

Instantly share code, notes, and snippets.

@Guendeli
Last active April 15, 2021 12:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Guendeli/99df688e28ce58fe598a0371c7865822 to your computer and use it in GitHub Desktop.
Save Guendeli/99df688e28ce58fe598a0371c7865822 to your computer and use it in GitHub Desktop.
Performance Utility variables with no accessor functions and bybe by byte string comparison
/// <summary>
/// Performance utility code
/// </summary>
public class Utils_Perf
{
/// <summary>
/// Static values with no accessor functions to slow them down.
/// Actually calling Vector3.zero is like calling New Vector3(0,0,0)
/// About 4x faster than Vector3.zero
/// </summary>
public static readonly Vector3 vec3_back = Vector3.back;
public static readonly Vector3 vec3_down = Vector3.down;
public static readonly Vector3 vec3_forward = Vector3.forward;
public static readonly Vector3 vec3_left = Vector3.left;
public static readonly Vector3 vec3_right = Vector3.right;
public static readonly Vector3 vec3_up = Vector3.up;
public static readonly Vector3 vec3_one = Vector3.one;
public static readonly Vector3 vec3_zero = Vector3.zero;
/// <summary>
/// Static values with no accessor functions to slow them down.
/// About 4x faster than Quaternion.identity
/// </summary>
public static readonly Quaternion quat_identity = Quaternion.identity;
/// <summary>
/// Does s1 start with s2?
/// Strict byte for byte comparison, nothing fancy.
/// About 100x as fast as String.StartsWith()
/// </summary>
/// <param name="s1">Longer string</param>
/// <param name="s2">Shorter string</param>
/// <returns>true, if s1 starts with s2</returns>
public static bool StartsWith(string s1, string s2)
{
return StartsWith(s1, s2, 0);
}
/// <summary>
/// Does s1 start with s2?
/// Strict byte for byte comparison, nothing fancy.
/// About 100x as fast as String.StartsWith()
/// </summary>
/// <param name="s1">Longer string</param>
/// <param name="s2">Shorter string</param>
/// <param name="startIndex">Start checking at this index of s1</param>
/// <returns>true, if s1 starts with s2</returns>
public static bool StartsWith(string s1, string s2, int startIndex)
{
if (string.IsNullOrEmpty(s1) || string.IsNullOrEmpty(s2))
return false;
// We need the index end
int s1Len = s1.Length - 1;
int s2Len = s2.Length - 1;
// Too short?
if (s1Len < s2Len)
return false;
for (int iChar = startIndex; iChar < s2Len; iChar++)
{
if (s1[iChar] != s2[iChar])
return false;
}
return true;
}
/// <summary>
/// Does s1 end with s2?
/// Strict byte for byte comparison, nothing fancy.
/// About 100x as fast as String.EndsWith()
/// </summary>
/// <param name="s1">Longer string</param>
/// <param name="s2">Shorter string</param>
/// <returns>true, if s1 ends with s2</returns>
public static bool EndsWith(string s1, string s2)
{
if (string.IsNullOrEmpty(s1) || string.IsNullOrEmpty(s2))
return false;
int s1Len = s1.Length - 1;
int s2Len = s2.Length - 1;
// Too short?
if (s1Len < s2Len)
return false;
for (int iChar = 0; iChar < s2Len; iChar++)
{
if (s1[s1Len - iChar] != s2[s2Len - iChar])
return false;
}
return true;
}
/// <summary>
/// About 3x faster than String.Contains()
///
/// Byte by byte comparison
/// Doesn't use language features of C#
///
/// With Equals(s1) vs. Equals(s1, StringComparison.Ordinal)
/// regular String.Equals() is much faster. The Ordinal
/// one calls into String.Compare() for some reason
/// which is much slower. Bizarre.
/// </summary>
/// <param name="s1"></param>
/// <param name="s2"></param>
/// <returns>true, if s1 contains s2 in it anywhere.</returns>
public static bool Contains(string s1, string s2)
{
if (string.IsNullOrEmpty(s1))
return false;
// s1 always contains an empty string
// Matching String.Contains() behavior
if (string.IsNullOrEmpty(s2))
return true;
int s1Len = s1.Length - 1;
int s2Len = s2.Length - 1;
int iChar2 = 0;
bool match = false;
for (int iChar1 = 0; iChar1 < s1Len; iChar1++)
{
// Found a possible match
if (s1[iChar1] == s2[0])
{
match = true;
// Loop until we find a mismatch.
// If we don't, then it matches.
for (iChar2 = 0; iChar2 < s2Len; iChar2++)
{
if (s1[iChar1 + iChar2] != s2[iChar2])
{
match = false;
break;
}
}
if (match)
return true;
}
}
return false;
}
// Usage :
//
// public enum TEST_KEY
// {
// thing1,
// thing2,
// thing3
// }
// Utils_Perf.EnumIntEqComp<TEST_KEY> noboxCompare;
// Dictionary<TEST_KEY, whatever> dictEnumIntNoBox =
// new Dictionary<TEST_KEY, whatever>(noboxCompare);
//
// https://stackoverflow.com/questions/26280788/dictionary-enum-key-performance
// todo; check if your TEnum is enum && typeCode == TypeCode.Int
/// <summary>
/// This code prevents the GC allocations when using Enum as
/// a key in a Dictionary or other collection.
/// </summary>
/// <typeparam name="TEnum">Key type</typeparam>
public struct EnumIntEqComp<TEnum> : IEqualityComparer<TEnum>
where TEnum : struct
{
public static class BoxAvoidance
{
public static readonly System.Func<TEnum, int> _wrapper;
public static int ToInt(TEnum enu)
{
return _wrapper(enu);
}
static BoxAvoidance()
{
var p = System.Linq.Expressions.Expression.Parameter(typeof(TEnum), null);
var c = System.Linq.Expressions.Expression.ConvertChecked(p, typeof(int));
_wrapper = System.Linq.Expressions.Expression.Lambda<System.Func<TEnum, int>>(c, p).Compile();
}
}
public bool Equals(TEnum firstEnum, TEnum secondEnum)
{
return BoxAvoidance.ToInt(firstEnum) ==
BoxAvoidance.ToInt(secondEnum);
}
public int GetHashCode(TEnum firstEnum)
{
return BoxAvoidance.ToInt(firstEnum);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment