#include "MyScript.h" #include "Engine/Level/Actor.h" #include "Engine/Core/Log.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Scripting/ManagedCLR/MUtils.h" #include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MEvent.h" #include "Engine/Scripting/ManagedCLR/MMethod.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Core/Collections/Sorting.h" #define NEW_VERSION 1 #if BUILD_DEBUG #define LONGER 1 #define CHUNK_TIMES 10 #else #define LONGER 10 #define CHUNK_TIMES 1000 #endif #define TIMES (10000*LONGER) #define TIMES2 (1000*LONGER) #define TIMES3 (100*LONGER) #define TIMES4 (10*LONGER) #define TIMES5 (1*LONGER) MyScript2::MyScript2(const SpawnParams& params) : Script(params) { // _tickUpdate = true; } MyScript::MyScript(const SpawnParams& params) : Script(params) { // _tickUpdate = true; } #define BENCHMARK_CALL_ARGS0(REPEAT, FUNC) \ { \ LOG(Info, #FUNC); \ const auto times = REPEAT; \ const auto chunkTimes = CHUNK_TIMES; \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ scriptTwo->FUNC(); \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - VirtualCall: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); \ } #define BENCHMARK_CALL_ARGS1(REPEAT, FUNC, PARAM1) \ { \ LOG(Info, #FUNC); \ const auto times = REPEAT; \ const auto chunkTimes = CHUNK_TIMES; \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ scriptTwo->FUNC(PARAM1); \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - VirtualCall: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); \ } #define BENCHMARK_CALL_ARGS1_REF(REPEAT, FUNC, PARAM1) \ { \ LOG(Info, #FUNC); \ const auto times = REPEAT; \ const auto chunkTimes = CHUNK_TIMES; \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ { \ auto _param1_orig_value = PARAM1; \ scriptTwo->FUNC(_param1_orig_value); \ } \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - VirtualCall: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); \ } #define BENCHMARK_CALL_ARGS5(REPEAT, FUNC, PARAM1, PARAM2, PARAM3, PARAM4, PARAM5) \ { \ LOG(Info, #FUNC); \ const auto times = REPEAT; \ const auto chunkTimes = CHUNK_TIMES; \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ scriptTwo->FUNC(PARAM1, PARAM2, PARAM3, PARAM4, PARAM5); \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - VirtualCall: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); \ } #define BENCHMARK_THUNK_BEGIN_ARGS0(REPEAT, FUNC) \ { \ const auto times = REPEAT; \ const auto chunkTimes = CHUNK_TIMES; \ typedef void (*Thunk)(void* instance, MObject** exception); \ const auto thunk = (Thunk)scriptTwo->GetClass()->GetMethod(#FUNC, 0)->GetThunk(); \ const auto instance = scriptTwo->GetOrCreateManagedInstance(); \ MObject* exception = nullptr; #define BENCHMARK_THUNK_CALL_ARGS0() \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ thunk(instance, &exception); \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - InvokeThunkOnly: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); #define BENCHMARK_THUNK_BEGIN_ARGS1(REPEAT, FUNC) \ { \ const auto times = REPEAT; \ const auto chunkTimes = CHUNK_TIMES; \ typedef void (*Thunk)(void* instance, void*, MObject** exception); \ const auto thunk = (Thunk)scriptTwo->GetClass()->GetMethod(#FUNC, 1)->GetThunk(); \ const auto instance = scriptTwo->GetOrCreateManagedInstance(); \ MObject* exception = nullptr; \ void* params[1]; #define BENCHMARK_THUNK_CALL_ARGS1() \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ thunk(instance, params[0], &exception); \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - InvokeThunkOnly: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); #define BENCHMARK_THUNK_CALL_ARGS1_REF(FUN) \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ { \ thunk(instance, params[0], &exception); \ FUN \ } \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - InvokeThunkOnly: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); #define BENCHMARK_THUNK_BEGIN_ARGS5(REPEAT, FUNC) \ { \ const auto times = REPEAT; \ const auto chunkTimes = CHUNK_TIMES; \ typedef void (*Thunk)(void* instance, void*, void*, void*, void*, void*, MObject** exception); \ const auto thunk = (Thunk)scriptTwo->GetClass()->GetMethod(#FUNC, 5)->GetThunk(); \ const auto instance = scriptTwo->GetOrCreateManagedInstance(); \ MObject* exception = nullptr; \ void* params[5]; #define BENCHMARK_THUNK_CALL_ARGS5() \ const auto freq = Platform::GetClockFrequency(); \ results.Clear(); \ auto start2 = Platform::GetTimeCycles(); \ for (int i = 0; i < times; ++i) \ { \ auto start = Platform::GetTimeCycles(); \ for (int j = 0; j < chunkTimes; ++j) \ thunk(instance, params[0], params[1], params[2], params[3], params[4], &exception); \ auto end = Platform::GetTimeCycles(); \ auto elapsed = end - start; \ results.Add(elapsed); \ } \ auto end2 = Platform::GetTimeCycles(); \ auto elapsed2 = end2 - start2; \ Sorting::MergeSort(results); \ const auto resultsIndex = 0; \ LOG(Info, " - InvokeThunkOnly: {:.1f}ns ({:.0f}ms total {} times)", (results[resultsIndex] * (1.0 / static_cast(freq)) * 1000000000 / chunkTimes), (elapsed2 * (1.0 / static_cast(freq)) * 1000), times*chunkTimes); #define BENCHMARK_THUNK_END() \ } void MyScript::OnStart() { LOG(Info, "C++ OnStart"); MyScript2* scriptTwo = (MyScript2*)GetActor()->GetScript(1); if (scriptTwo == nullptr) return; bool otherTests = true; bool arrayTests = true; bool asRefTests = true; Array results(TIMES); auto CyclesToSeconds = 1.0 / static_cast(Platform::GetClockFrequency()); LOG(Info, "CyclesToSeconds: {}, freq: {}", CyclesToSeconds, Platform::GetClockFrequency()); String shortString = TEXT("Testing string parameter marshalling"); StringAnsi shortStringAnsi = "Testing string parameter marshalling"; Actor* actor = GetActor(); SceneReference sceneRef; sceneRef.ID = actor->GetScene()->GetID(); BehaviorUpdateContext behaviorUpdateContext = {}; RenderContext renderContext = MainRenderTask::Instance; renderContext.List = RenderList::GetFromPool(); Array simpleArray; for (int i=0; i<10; i++) simpleArray.Add(i); Array actorArray; for (int i=0; i<10; i++) actorArray.Add(actor); TestStruct testStruct; testStruct.Object = actor; testStruct.SceneRef = sceneRef; Array complexArray; for (int i=0; i<10; i++) complexArray.Add(testStruct); if (otherTests) { BENCHMARK_CALL_ARGS0(TIMES2, SimpleCall); BENCHMARK_THUNK_BEGIN_ARGS0(TIMES2, SimpleCall); BENCHMARK_THUNK_CALL_ARGS0(); BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS5(TIMES4, SimpleParams, 1, 2, '3', 4, 5); BENCHMARK_THUNK_BEGIN_ARGS5(TIMES3, SimpleParams); params[0] = MUtils::Box(1, MCore::TypeCache::Int32); params[1] = MUtils::Box(2, MCore::TypeCache::Single); params[2] = MUtils::Box('3', MCore::TypeCache::SByte); params[3] = MUtils::Box(4, MCore::TypeCache::Double); params[4] = MUtils::Box(5, MCore::TypeCache::Int64); BENCHMARK_THUNK_CALL_ARGS5(); #if NEW_VERSION auto __param0_handle = *(MGCHandle*)¶ms[0]; MCore::GCHandle::Free(__param0_handle); auto __param1_handle = *(MGCHandle*)¶ms[1]; MCore::GCHandle::Free(__param1_handle); auto __param2_handle = *(MGCHandle*)¶ms[2]; MCore::GCHandle::Free(__param2_handle); auto __param3_handle = *(MGCHandle*)¶ms[3]; MCore::GCHandle::Free(__param3_handle); auto __param4_handle = *(MGCHandle*)¶ms[4]; MCore::GCHandle::Free(__param4_handle); #endif BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES3, StringParamAnsi, shortStringAnsi); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES2, StringParamAnsi); params[0] = MUtils::Box(shortStringAnsi, MCore::TypeCache::String); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION auto __param0_handle = *(MGCHandle*)¶ms[0]; MCore::GCHandle::Free(__param0_handle); #endif BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES3, StringParam, shortString); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES2, StringParam); params[0] = MUtils::Box(shortString, MCore::TypeCache::String); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION auto __param0_handle = *(MGCHandle*)¶ms[0]; MCore::GCHandle::Free(__param0_handle); #endif BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES3, StringParamRef, shortString); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES2, StringParamRef); params[0] = MUtils::Box(shortString, MCore::TypeCache::String); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION auto __param0_handle = *(MGCHandle*)¶ms[0]; MCore::GCHandle::Free(__param0_handle); #endif BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES3, StringParamRefConst, shortString); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES2, StringParamRefConst); params[0] = MUtils::Box(shortString, MCore::TypeCache::String); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION auto __param0_handle = *(MGCHandle*)¶ms[0]; MCore::GCHandle::Free(__param0_handle); #endif BENCHMARK_THUNK_END(); } if (asRefTests) { String str = shortString; BENCHMARK_CALL_ARGS1_REF(TIMES3, StringParamAsRef, str); LOG(Info, " - InvokeThunkOnly: not valid"); /* str = shortString; BENCHMARK_THUNK_BEGIN_ARGS1(TIMES3, StringParamAsRef); #if NEW_VERSION auto __param_orig_str = MUtils::Box(str, MCore::TypeCache::String); auto __param_str = __param_orig_str; params[0] = &__param_str; #else auto __param_str = MUtils::Box(str, MCore::TypeCache::String); params[0] = &__param_str; #endif BENCHMARK_THUNK_CALL_ARGS1_REF({ params[0] = &__param_str; }); str = MUtils::Unbox(*(MObject**)params[0]); ASSERT(str == shortString); #if NEW_VERSION MUtils::FreeManaged((MObject*)__param_str); if (__param_orig_str != __param_str) MUtils::FreeManaged((MObject*)__param_orig_str); #endif BENCHMARK_THUNK_END();*/ } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES2, ActorParam, actor); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES2, ActorParam); params[0] = ScriptingObject::ToManaged((ScriptingObject*)actor); BENCHMARK_THUNK_CALL_ARGS1(); BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES3, ComplexParam, behaviorUpdateContext); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES2, ComplexParam); params[0] = MUtils::Box(behaviorUpdateContext, BehaviorUpdateContext::TypeInitializer.GetClass()); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION MUtils::FreeManaged((MObject*)params[0]); #endif BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES4, Complex2Param, renderContext); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES3, Complex2Param); params[0] = MUtils::Box(renderContext, RenderContext::TypeInitializer.GetClass()); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION MUtils::FreeManaged((MObject*)params[0]); #endif BENCHMARK_THUNK_END(); } if (otherTests) { BENCHMARK_CALL_ARGS1(TIMES4, Complex2ParamConst, renderContext); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES3, Complex2ParamConst); params[0] = MUtils::Box(renderContext, RenderContext::TypeInitializer.GetClass()); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION MUtils::FreeManaged((MObject*)params[0]); #endif BENCHMARK_THUNK_END(); } if (asRefTests) { RenderContext context = renderContext; BENCHMARK_CALL_ARGS1_REF(TIMES4, Complex2ParamAsRef, context); LOG(Info, " - InvokeThunkOnly: not valid"); /* context = renderContext; BENCHMARK_THUNK_BEGIN_ARGS1(TIMES3, Complex2ParamAsRef); #if NEW_VERSION auto __param_orig_context = MUtils::Box(context, RenderContext::TypeInitializer.GetClass()); auto __param_context = __param_orig_context; params[0] = &__param_context; #else auto __param_context = MUtils::Box(context, RenderContext::TypeInitializer.GetClass()); params[0] = &__param_context; #endif BENCHMARK_THUNK_CALL_ARGS1_REF({ if (__param_orig_context != __param_context) MUtils::FreeManaged((MObject*)__param_orig_context); params[0] = &__param_context; }); context = MUtils::Unbox(*(MObject**)params[0]); ASSERT(context.Buffers == renderContext.Buffers); ASSERT(context.Task == renderContext.Task); #if NEW_VERSION MUtils::FreeManaged((MObject*)__param_context); if (__param_orig_context != __param_context) MUtils::FreeManaged((MObject*)__param_orig_context); #endif BENCHMARK_THUNK_END();*/ } if (arrayTests) { BENCHMARK_CALL_ARGS1(TIMES4, SimpleArrayParam, simpleArray); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES3, SimpleArrayParam); params[0] = MUtils::ToArray(simpleArray, MCore::TypeCache::Int32); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION //TODO #endif BENCHMARK_THUNK_END(); } if (arrayTests && asRefTests) { Array arr = simpleArray; BENCHMARK_CALL_ARGS1_REF(TIMES4, SimpleArrayParamAsRef, simpleArray); LOG(Info, " - InvokeThunkOnly: not valid"); /* arr = simpleArray; BENCHMARK_THUNK_BEGIN_ARGS1(TIMES3, SimpleArrayParamAsRef); #if NEW_VERSION auto __param_orig_arr = MUtils::ToArray(arr, MCore::TypeCache::Int32); auto __param_arr = __param_orig_arr; params[0] = &__param_arr; #else auto __param_arr = MUtils::ToArray(arr, MCore::TypeCache::Int32); params[0] = &__param_arr; #endif BENCHMARK_THUNK_CALL_ARGS1(); arr = MUtils::Unbox>(*(MObject**)params[0]); ASSERT(arr.Count() == simpleArray.Count()); for (int i=0; i((MObject*)params[0]); #endif BENCHMARK_THUNK_END(); } if (arrayTests) { BENCHMARK_CALL_ARGS1(TIMES4, ComplexArrayParam, complexArray); BENCHMARK_THUNK_BEGIN_ARGS1(TIMES3, ComplexArrayParam); params[0] = MUtils::ToArray(complexArray, TestStruct::TypeInitializer.GetClass()); BENCHMARK_THUNK_CALL_ARGS1(); #if NEW_VERSION //MUtils::FreeManaged((MObject*)params[0]); #endif BENCHMARK_THUNK_END(); } if (arrayTests && asRefTests) { Array arr = complexArray; BENCHMARK_CALL_ARGS1_REF(TIMES4, ComplexArrayParamAsRef, arr); LOG(Info, " - InvokeThunkOnly: not valid"); /* arr = complexArray; BENCHMARK_THUNK_BEGIN_ARGS1(TIMES4, ComplexArrayParamAsRef); #if NEW_VERSION auto __param_orig_arr = MUtils::ToArray(arr, TestStruct::TypeInitializer.GetClass()); auto __param_arr = __param_orig_arr; params[0] = &__param_arr; #else auto __param_arr = MUtils::ToArray(arr, TestStruct::TypeInitializer.GetClass()); params[0] = &__param_arr; #endif BENCHMARK_THUNK_CALL_ARGS1(); arr = MUtils::Unbox>(*(MObject**)params[0]); ASSERT(arr.Count() == complexArray.Count()); for (int i = 0; i < arr.Count(); i++) ASSERT(arr[i] == complexArray[i]); #if NEW_VERSION //MUtils::FreeManaged((MObject*)params[0]); #endif BENCHMARK_THUNK_END();*/ } } void MyScript::OnEnable() { //LOG(Info, "C++ OnEnable"); } void MyScript::OnDisable() { LOG(Info, "running garbage collector"); MCore::GC::Collect(MCore::GC::MaxGeneration(), MGCCollectionMode::Aggressive, true, true); MCore::GC::WaitForPendingFinalizers(); } void MyScript::OnUpdate() { }