Skip to content

Commit

Permalink
Smaller performance improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
genaray committed Jul 15, 2024
1 parent 87f2105 commit e7ba793
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
4 changes: 4 additions & 0 deletions src/Arch.SourceGen/Queries/QueryDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static StringBuilder AppendQueryDescriptionWithAll(this StringBuilder sb,
public ref QueryDescription WithAll<{{generics}}>()
{
All = Group<{{generics}}>.Types;
_hashCode = -1;
return ref this;
}
""";
Expand Down Expand Up @@ -52,6 +53,7 @@ public static StringBuilder AppendQueryDescriptionWithAny(this StringBuilder sb,
public ref QueryDescription WithAny<{{generics}}>()
{
Any = Group<{{generics}}>.Types;
_hashCode = -1;
return ref this;
}
""";
Expand Down Expand Up @@ -81,6 +83,7 @@ public static StringBuilder AppendQueryDescriptionWithNone(this StringBuilder sb
public ref QueryDescription WithNone<{{generics}}>()
{
None = Group<{{generics}}>.Types;
_hashCode = -1;
return ref this;
}
""";
Expand Down Expand Up @@ -110,6 +113,7 @@ public static StringBuilder AppendQueryDescriptionWithExclusive(this StringBuild
public ref QueryDescription WithExclusive<{{generics}}>()
{
Exclusive = Group<{{generics}}>.Types;
_hashCode = -1;
return ref this;
}
""";
Expand Down
8 changes: 7 additions & 1 deletion src/Arch/Core/Chunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ public partial struct Chunk
/// </summary>
/// <typeparam name="T">The componen type.</typeparam>
/// <returns>The index in the <see cref="Components"/> array.</returns>
[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Pure]
private int Index<T>()
Expand All @@ -250,12 +251,14 @@ private int Index<T>()
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <returns>The array.</returns>
[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Pure]
public T[] GetArray<T>()
{
var index = Index<T>();
Debug.Assert(index != -1 && index < Components.Length, $"Index is out of bounds, component {typeof(T)} with id {index} does not exist in this chunk.");

var array = Components.DangerousGetReferenceAt(index);
return Unsafe.As<T[]>(array);
}
Expand All @@ -266,18 +269,21 @@ public T[] GetArray<T>()
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <returns>The array <see cref="Span{T}"/>.</returns>
[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Pure]
public Span<T> GetSpan<T>()
{
return new Span<T>(GetArray<T>());
var array = GetArray<T>();
return MemoryMarshal.CreateSpan(ref array[0], array.Length);
}

/// <summary>
/// Returns a reference to the first element of a component from its component array in an unsafe manner.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <returns>A reference to the first element.</returns>
[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Pure]
public ref T GetFirst<T>()
Expand Down
54 changes: 49 additions & 5 deletions src/Arch/Core/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,38 @@ namespace Arch.Core;
[SkipLocalsInit]
public partial struct QueryDescription : IEquatable<QueryDescription>
{

/// <summary>
/// A null reference, basically an empty <see cref="QueryDescription"/> that queries for all <see cref="Entity"/>s.
/// </summary>
public static readonly QueryDescription Null = new();

/// <summary>
/// A cached hash code that is used to find the matching <see cref="Query"/> for this instance.
/// </summary>
private int _hashCode;

/// <summary>
/// An array of all components that an <see cref="Entity"/> should have mandatory.
/// <remarks>If the content of the array is subsequently changed, a <see cref="Rebuild"/> should be carried out.</remarks>
/// </summary>
public ComponentType[] All = Array.Empty<ComponentType>();

/// <summary>
/// An array of all components of which an <see cref="Entity"/> should have at least one.
/// <remarks>If the content of the array is subsequently changed, a <see cref="Rebuild"/> should be carried out.</remarks>
/// </summary>
public ComponentType[] Any = Array.Empty<ComponentType>();

/// <summary>
/// An array of all components of which an <see cref="Entity"/> should not have any.
/// <remarks>If the content of the array is subsequently changed, a <see cref="Rebuild"/> should be carried out.</remarks>
/// </summary>
public ComponentType[] None = Array.Empty<ComponentType>();

/// <summary>
/// An array of all components that exactly match the structure of an <see cref="Entity"/>.
/// <see cref="Entity"/>'s with more or less components than those defined in the array are not addressed.
/// <remarks>If the content of the array is subsequently changed, a <see cref="Rebuild"/> should be carried out.</remarks>
/// </summary>
public ComponentType[] Exclusive = Array.Empty<ComponentType>();

Expand All @@ -44,6 +52,33 @@ public partial struct QueryDescription : IEquatable<QueryDescription>
/// </summary>
public QueryDescription() { }

/// <summary>
/// Initializes a new instance of the <see cref="QueryDescription"/> struct.
/// </summary>
/// <param name="all">An array of all components that an <see cref="Entity"/> should have mandatory.</param>
/// <param name="any">An array of all components of which an <see cref="Entity"/> should have at least one.</param>
/// <param name="none">An array of all components of which an <see cref="Entity"/> should not have any.</param>
/// <param name="exclusive">All components that an <see cref="Entity"/> should have mandatory.</param>
public QueryDescription(ComponentType[]? all = null, ComponentType[]? any = null, ComponentType[]? none = null, ComponentType[]? exclusive = null)
{
All = all ?? Array.Empty<ComponentType>();
Any = any ?? Array.Empty<ComponentType>();
None = none ?? Array.Empty<ComponentType>();
Exclusive = exclusive ?? Array.Empty<ComponentType>();
_hashCode = GetHashCode();
}

/// <summary>
/// Recreates this instance by calculating a new <see cref="_hashCode"/>.
/// Is actually only needed if the passed arrays are changed afterwards.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Rebuild()
{
_hashCode = -1;
_hashCode = GetHashCode();
}

/// <summary>
/// All components that an <see cref="Entity"/> should have mandatory.
/// </summary>
Expand All @@ -54,6 +89,7 @@ public QueryDescription() { }
public ref QueryDescription WithAll<T>()
{
All = Group<T>.Types;
_hashCode = -1;
return ref this;
}

Expand All @@ -67,6 +103,7 @@ public ref QueryDescription WithAll<T>()
public ref QueryDescription WithAny<T>()
{
Any = Group<T>.Types;
_hashCode = -1;
return ref this;
}

Expand All @@ -80,6 +117,7 @@ public ref QueryDescription WithAny<T>()
public ref QueryDescription WithNone<T>()
{
None = Group<T>.Types;
_hashCode = -1;
return ref this;
}

Expand All @@ -94,6 +132,7 @@ public ref QueryDescription WithNone<T>()
public ref QueryDescription WithExclusive<T>()
{
Exclusive = Group<T>.Types;
_hashCode = -1;
return ref this;
}

Expand All @@ -104,10 +143,7 @@ public ref QueryDescription WithExclusive<T>()
/// <returns>True if elements of the arrays are equal, otherwhise false.</returns>
public bool Equals(QueryDescription other)
{
var allHash = Component.GetHashCode(All);
var anyHash = Component.GetHashCode(Any);
var noneHash = Component.GetHashCode(None);
return allHash == Component.GetHashCode(other.All) && anyHash == Component.GetHashCode(other.Any) && noneHash == Component.GetHashCode(other.None);
return GetHashCode() == other.GetHashCode();
}

/// <summary>
Expand All @@ -127,13 +163,21 @@ public override bool Equals(object? obj)
/// <returns>The hash.</returns>
public override int GetHashCode()
{
// Cache hashcode since the calculation is expensive.
if (_hashCode != -1)
{
return _hashCode;
}

unchecked
{
// Overflow is fine, just wrap{
var hash = 17;
hash = (hash * 23) + All.GetHashCode();
hash = (hash * 23) + Any.GetHashCode();
hash = (hash * 23) + None.GetHashCode();
hash = (hash * 23) + Exclusive.GetHashCode();
_hashCode = hash;
return hash;
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Arch/Pollyfilling/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit {}
}

0 comments on commit e7ba793

Please sign in to comment.