Code is below. Results:
ManagedWriteByte avg time elapsed: 8,21948E-07
ManagedReadByte avg time elapsed: 7,098913E-07
ManagedWriteUlong avg time elapsed: 1,907349E-08
ManagedReadUlong avg time elapsed: 1,811981E-08
UnsafeWriteByte avg time elapsed: 9,460449E-07
UnsafeReadByte avg time elapsed: 9,717942E-07
UnsafeWriteUlong avg time elapsed: 1,016855E-07
UnsafeReadUlong avg time elapsed: 1,052618E-07
Unsure why Managed*Ulong
get the best times? If reading / writing byte
takes about the same time for managed vs. unmanaged/unsafe code, why do we see a substantial difference for ulong
?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
//Showcases the difference betweeen Managed vs. Unsafe NativeArray short (byte) and long (ulong) accesses.
//Set all bits in writes, then retrieve these into local members so that they have an effect / aren't stripped by compiler.
public class RWPerfDemo : MonoBehaviour
{
byte[] bytesManage = null;
ulong[] ulongsManage = null;
NativeArray<byte> bytesUnsafe;
NativeArray<ulong> ulongsUnsafe;
const int iterationsPerFunc = 1000;
const int iterationsLoopByte = 256; //should be max unrollable, basically.
const int iterationsLoopUlong = iterationsLoopByte / 8; //should be max unrollable, basically.
byte b = 0;
ulong u = 0;
void Start()
{
int totalBytes = iterationsLoopByte;
bytesManage = new byte [totalBytes];
ulongsManage = new ulong[totalBytes / 8];
ulongsUnsafe = new NativeArray<ulong>(totalBytes / 8, Allocator.Persistent);
ManagedWriteByte ("ManagedWriteByte");
ManagedReadByte ("ManagedReadByte");
ManagedWriteUlong("ManagedWriteUlong");
ManagedReadUlong ("ManagedReadUlong");
UnsafeWriteByte ("UnsafeWriteByte");
UnsafeReadByte ("UnsafeReadByte");
UnsafeWriteUlong ("UnsafeWriteUlong");
UnsafeReadUlong ("UnsafeReadUlong");
ulongsUnsafe.Dispose();
}
//MANAGED...
void ManagedWriteByte(string name)
{
float t0 = Time.realtimeSinceStartup;
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopByte; l++)
{
bytesManage[l] = byte.MaxValue;
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
void ManagedReadByte(string name)
{
float t0 = Time.realtimeSinceStartup;
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopByte; l++)
{
b = bytesManage[l];
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
void ManagedWriteUlong(string name)
{
float t0 = Time.realtimeSinceStartup;
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopUlong / 8; l++)
{
ulongsManage[l] = ulong.MaxValue;
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
void ManagedReadUlong(string name)
{
float t0 = Time.realtimeSinceStartup;
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopUlong / 8; l++)
{
u = ulongsManage[l];
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
//...MANAGED
//UNSAFE...
void UnsafeWriteByte(string name)
{
float t0 = Time.realtimeSinceStartup;
unsafe {
ulong* ulongs = (ulong*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks<ulong>(ulongsUnsafe);
byte* bytes = (byte*) ulongs;
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopByte; l++)
{
bytes[l] = byte.MaxValue;
}
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
void UnsafeReadByte(string name)
{
float t0 = Time.realtimeSinceStartup;
unsafe {
ulong* ulongs = (ulong*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks<ulong>(ulongsUnsafe);
byte* bytes = (byte*) ulongs;
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopByte; l++)
{
b = bytes[l];
}
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
void UnsafeWriteUlong(string name)
{
float t0 = Time.realtimeSinceStartup;
unsafe {
ulong* ulongs = (ulong*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks<ulong>(ulongsUnsafe);
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopUlong; l++)
{
ulongs[l] = ulong.MaxValue;
}
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
void UnsafeReadUlong(string name)
{
float t0 = Time.realtimeSinceStartup;
unsafe {
ulong* ulongs = (ulong*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks<ulong>(ulongsUnsafe);
for (int i = 0; i < iterationsPerFunc; i++)
{
for (int l = 0; l < iterationsLoopUlong; l++)
{
u = ulongs[l];
}
}
}
float t1 = Time.realtimeSinceStartup;
Debug.Log(name+" avg time elapsed: "+(t1 - t0) / (float) iterationsPerFunc);
}
//...UNSAFE
}