DotRecast
DotRecast copied to clipboard
SOH allocation issues
Rider keeps telling me that Small Object Heap allocation is high for the DtCrowd.Update method.
This is my usage
Where the Update method is called every 0.3...
On the pictures above the compiler is complaining about 200mb-ish, but I've seen it complaining about almost 800mb. Do you have any idea of what could it be?
I'll check and mention you!!
@Danil0v3s
Using stack memory introduces some potential risks that need careful consideration. Could you please review the following changes and confirm their appropriateness?
before
// Build sampling pattern aligned to desired velocity.
float[] pat = new float[(DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2];
after
// Build sampling pattern aligned to desired velocity.
int patSize = (DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2;
RcStackArray512<float> pat = RcStackArray512<float>.Empty;
ThrowHelper.ThrowExceptionIfIndexOutOfRange(patSize - 1, pat.Length);
@Danil0v3s
Using stack memory introduces some potential risks that need careful consideration.
Could you please review the following changes and confirm their appropriateness?
before
// Build sampling pattern aligned to desired velocity. float[] pat = new float[(DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2];after
// Build sampling pattern aligned to desired velocity. int patSize = (DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2; RcStackArray512<float> pat = RcStackArray512<float>.Empty; ThrowHelper.ThrowExceptionIfIndexOutOfRange(patSize - 1, pat.Length);
Hey, thanks for taking the time to look at the issue. The solution is a bit above my league so I can't really say anything. But if you wish to release a preview version I can see if the SOH warnings are gone
I added RcRentedArray, which internally utilizes the functionality of ArrayPool<T>. If everything seems fine with its features and usage, I will release it and mention you.
Thank you for reporting the issue.
[Test]
public void Test()
{
var rand = new RcRand();
for (int loop = 0; loop < 1024; ++loop)
{
int length = (int)(rand.Next() * 2048);
var values = RandomValues(length);
using var array = RcRentedArray.RentDisposableArray<float>(length);
for (int i = 0; i < array.Length; ++i)
{
array[i] = values[i];
}
for (int i = 0; i < array.Length; ++i)
{
Assert.That(array[i], Is.EqualTo(values[i]));
}
Assert.That(array[^1], Is.EqualTo(values[^1]));
Assert.Throws<IndexOutOfRangeException>(() => array[-1] = 0);
Assert.Throws<IndexOutOfRangeException>(() => array[array.Length + 1] = 0);
Assert.Throws<IndexOutOfRangeException>(() => _ = array[-1]);
Assert.Throws<IndexOutOfRangeException>(() => _ = array[array.Length + 1]);
}
}
next !!
in DtNodePool.GetNode()
in DtNavMesh.GetPolyHeight()
in DtPathUtils.MergeCorridorStartMoved()
in RcRentedArray()
in DtNavMeshQuery.Raycast()
new Vector3f[3] in DtNavMesh.GetPolyHeight()
DtNodePool.GetNode()
new DtRaycastHit() in DtNavMeshQuery.Raycast()
result.AddRange(path.GetRange(furthestPath, path.Count - furthestPath)) in DtPathUtils.MergeCorridorStartMoved()
- use stackalloc keywork
- use NativeMemory
https://github.com/bepu/bepuphysics2
- use stackalloc keywork
- use NativeMemory
https://github.com/bepu/bepuphysics2
Thanks for your input! However, to support various compatibility/platforms/debugging, we cannot use unmanaged code.
Is there any other way? I'm currently testing using ArrayPool<T>, there's a slight slowdown in speed.
- use stackalloc keywork
- use NativeMemory
https://github.com/bepu/bepuphysics2
Thanks for your input! However, to support various compatibility/platforms/debugging, we cannot use unmanaged code.
Is there any other way? I'm currently testing using ArrayPool, there's a slight slowdown in speed.
If you mark the assembly or whatever it's called as unsafe, on the user side we would need to allow usage of unsafe code, no? So I guess it's possible?
Or is there more to it than enabling the unsafe code usage?
- use stackalloc keywork
- use NativeMemory
https://github.com/bepu/bepuphysics2
Thanks for your input! However, to support various compatibility/platforms/debugging, we cannot use unmanaged code. Is there any other way? I'm currently testing using ArrayPool, there's a slight slowdown in speed.
If you mark the assembly or whatever it's called as unsafe, on the user side we would need to allow usage of unsafe code, no? So I guess it's possible?
Or is there more to it than enabling the unsafe code usage?
From a library perspective, I'm trying to develop only safe code. First, let’s fix the SOH that occurs structurally.
- use stackalloc keywork
- use NativeMemory
https://github.com/bepu/bepuphysics2
Thanks for your input! However, to support various compatibility/platforms/debugging, we cannot use unmanaged code.
Is there any other way? I'm currently testing using ArrayPool, there's a slight slowdown in speed.
Unsafe code is cross platform, and anywhere in bcl and unity (UnsafeUtility/Unity, NativeMemory/DotNet), but maybe cause memory leak.
Use unsafe code in lib(gc free), expose Safe/Unsafe api for user code is a good way.
for example
unsafe int Physics.Raycast(Span<HitResult> outHits){
for(int i = 0, i < outHits.Length; i++)
outHits[i] = new HitResult();
return outHits.Length;
}
// use managed collection
var list = new List<HitResult>(4);
CollectionsMarshal.SetCount(list, 4);
var span = CollectionsMarshal.AsSpan(list);
// Span<T> span = stackalloc HitResult[4]; // if temp
var hitCount = Physics.Raycast(span);
// use unsafe collection // need unsafe blocks
unsafe{
var ptr = NativeMemory.Alloc(sizeof(HitReuslt) * 4);
Span<HitReuslt> span = new Span<HitReuslt>(ptr, 4);
var hitCount = Physics.Raycast(span);
NativeMemory.Free(ptr);
}
If dont want to use unsafe code
- Span<T> temp = stackalloc T[Size] for small temp array
- ArrayPool<T>.Return(array, clearArray), pass clearArray by false if T is UnmanagedStruct(RuntimeHelpers.IsReferenceOrContainsReferences)
- Use Span and Unsafe.Add/MemoryMarshal/CollectionsMarshal api to improve performance
@Sarofc I will check if there is any performance improvement through benchmarking.
benchmark source map https://github.com/ikpil/DotRecast/issues/10#issuecomment-1925555592
before
Nice!! It seems we're down to 12 issues in other places.
Here's my project my project so you can play around since I don't discard the possiblity I'm doing things in a wrong way. If you change the postgres address to point to local and user the docker-compose inside deploy it should work just fine. A manual migration of EF still needs to be triggered
version 2024.1.2
- Ryzen 5600X
version 2024.1.2
- Rayzen 5625U
Hey @ikpil, what are we looking for exactly in those comparison images? Would you write the comparison numbers down? I've tried looking for the same data in the pictures but it seems they change place
The value currently being compared is the Avg Update Time
If SOH issues are improperly addressed, there tends to be a decline in performance. We will compare 2024.1.2 with the upcoming 2024.1.3.
2024.1.3
- Ryzen 5600X
- reuse DtNode