diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index f5ba6714..4c0b5553 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -854,6 +854,10 @@ RelativePath="..\smn_player.cpp" > + + diff --git a/core/smn_profiler.cpp b/core/smn_profiler.cpp new file mode 100644 index 00000000..4194b2dc --- /dev/null +++ b/core/smn_profiler.cpp @@ -0,0 +1,168 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#include "sm_globals.h" +#include "HandleSys.h" + +//Note: Do not add this to Linux yet, i haven't done the HPET timing research (if even available) +//nonetheless we need accurate counting +#if defined PLATFORM_LINUX +#error "Not supported" +#endif + +struct Profiler +{ + Profiler() + { + started = false; + stopped = false; + } +#if defined PLATFORM_WINDOWS + LARGE_INTEGER start; + LARGE_INTEGER end; + double freq; +#endif + bool started; + bool stopped; +}; + +HandleType_t g_ProfilerType = 0; + +class ProfilerHelpers : + public SMGlobalClass, + public IHandleTypeDispatch +{ +public: + void OnSourceModAllInitialized() + { + g_ProfilerType = g_HandleSys.CreateType("Profiler", this, 0, NULL, NULL, g_pCoreIdent, NULL); + } + void OnSourceModShutdown() + { + g_HandleSys.RemoveType(g_ProfilerType, g_pCoreIdent); + } + void OnHandleDestroy(HandleType_t type, void *object) + { + Profiler *prof = (Profiler *)object; + delete prof; + } +} s_ProfHelpers; + +static cell_t CreateProfiler(IPluginContext *pContext, const cell_t *params) +{ + Profiler *p = new Profiler(); + +#if defined PLATFORM_WINDOWS + LARGE_INTEGER qpf; + QueryPerformanceFrequency(&qpf); + p->freq = 1.0 / (double)(qpf.QuadPart); +#endif + + Handle_t hndl = g_HandleSys.CreateHandle(g_ProfilerType, p, pContext->GetIdentity(), g_pCoreIdent, NULL); + if (hndl == BAD_HANDLE) + { + delete p; + } + + return hndl; +} + +static cell_t StartProfiling(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = params[1]; + HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent); + HandleError err; + Profiler *prof; + + if ((err = g_HandleSys.ReadHandle(hndl, g_ProfilerType, &sec, (void **)&prof)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err); + } + +#if defined PLATFORM_WINDOWS + QueryPerformanceCounter(&prof->start); +#endif + + prof->started = true; + prof->stopped = false; + + return 1; +} + +static cell_t StopProfiling(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = params[1]; + HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent); + HandleError err; + Profiler *prof; + + if ((err = g_HandleSys.ReadHandle(hndl, g_ProfilerType, &sec, (void **)&prof)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err); + } + + if (!prof->started) + { + return pContext->ThrowNativeError("Profiler was never started"); + } + +#if defined PLATFORM_WINDOWS + QueryPerformanceCounter(&prof->end); +#endif + + prof->started = false; + prof->stopped = true; + + return 1; +} + +static cell_t GetProfilerTime(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = params[1]; + HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent); + HandleError err; + Profiler *prof; + + if ((err = g_HandleSys.ReadHandle(hndl, g_ProfilerType, &sec, (void **)&prof)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err); + } + + if (!prof->stopped) + { + return pContext->ThrowNativeError("Profiler was never stopped"); + } + + float fTime; + +#if defined PLATFORM_WINDOWS + LONGLONG diff = prof->end.QuadPart - prof->start.QuadPart; + double seconds = diff * prof->freq; + fTime = (float)seconds; +#endif + + return sp_ftoc(fTime); +} + +REGISTER_NATIVES(profilerNatives) +{ + {"CreateProfiler", CreateProfiler}, + {"GetProfilerTime", GetProfilerTime}, + {"StartProfiling", StartProfiling}, + {"StopProfiling", StopProfiling}, + {NULL, NULL}, +}; diff --git a/plugins/include/profiler.inc b/plugins/include/profiler.inc new file mode 100644 index 00000000..74fa317a --- /dev/null +++ b/plugins/include/profiler.inc @@ -0,0 +1,60 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is part of the SourceMod/SourcePawn SDK. This file may only be used + * or modified under the Terms and Conditions of its License Agreement, which is found + * in LICENSE.txt. The Terms and Conditions for making SourceMod extensions/plugins + * may change at any time. To view the latest information, see: + * http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#if defined _profiler_included + #endinput +#endif +#define _profiler_included + +/** + * ONLY AVAILABLE ON WINDOWS RIGHT NOW K. + */ + +/** + * Creates a new profile object. The Handle must be freed + * using CloseHandle(). + * + * @return Handle to the profiler object. + */ +native Handle:CreateProfiler(); + +/** + * Starts profiling. + * + * @param prof Profiling object. + * @noreturn + * @error Invalid Handle. + */ +native StartProfiling(Handle:prof); + +/** + * Stops profiling. + * + * @param prof Profiling object. + * @noreturn + * @error Invalid Handle or profiling was never started. + */ +native StopProfiling(Handle:prof); + +/** + * Returns the amount of high-precision time in seconds + * that passed during the profiler's last start/stop + * cycle. + * + * @param prof Profiling object. + * @return Time elapsed in seconds. + * @error Invalid Handle. + */ +native Float:GetProfilerTime(Handle:prof); diff --git a/plugins/testsuite/benchmark.sp b/plugins/testsuite/benchmark.sp new file mode 100644 index 00000000..7d39dfab --- /dev/null +++ b/plugins/testsuite/benchmark.sp @@ -0,0 +1,157 @@ +#include +#include + +public Plugin:myinfo = +{ + name = "Benchmarks", + author = "AlliedModders LLC", + description = "Basic benchmarks", + version = "1.0.0.0", + url = "http://www.sourcemod.net/" +}; + +#define MATH_INT_LOOPS 2000 +#define MATH_FLOAT_LOOPS 2000 +#define STRING_OP_LOOPS 2000 +#define STRING_FMT_LOOPS 2000 +#define STRING_ML_LOOPS 2000 +#define STRING_RPLC_LOOPS 2000 + +new Float:g_dict_time +new Handle:g_Prof = INVALID_HANDLE + +public OnPluginStart() +{ + RegServerCmd("bench", Benchmark); + g_Prof = CreateProfiler(); + StartProfiling(g_Prof); + LoadTranslations("fakedict-sourcemod.cfg"); + StopProfiling(g_Prof); + g_dict_time = GetProfilerTime(g_Prof); +} + +public Action:Benchmark(args) +{ + PrintToServer("dictionary time: %f seconds", g_dict_time); + StringBench(); + MathBench(); + return Plugin_Handled; +} + +MathBench() +{ + StartProfiling(g_Prof); + new iter = MATH_INT_LOOPS; + new a, b, c; + while(iter--) + { + a = iter * 7; + b = 5 + iter; + c = 6 / (iter + 3); + a = 6 * (iter); + b = a * 185; + a = b / 25; + c = b - a + 3; + b = b*b; + a = (a + c) / (b - c); + b = 6; + c = 1; + b = a * 128 - c; + c = b * (a + 16) * b; + if (!a) + { + a = 5; + } + a = c + (28/a) - c; + } + StopProfiling(g_Prof); + PrintToServer("int benchmark: %f seconds", GetProfilerTime(g_Prof)); + + StartProfiling(g_Prof); + new Float:fa, Float:fb, Float:fc + new int1 + iter = MATH_FLOAT_LOOPS; + while (iter--) + { + fa = iter * 0.7; + fb = 5.1 + iter; + fc = 6.1 / (float(iter) + 2.5); + fa = 6.1 * (iter); + fb = fa * 185.26; + fa = fb / 25.56; + fc = fb - a + float(3); + fb = fb*fb; + fa = (fa + fc) / (fb - fc); + fb = 6.2; + fc = float(1); + int1 = RoundToNearest(fa); + fb = fa * float(128) - int1; + fc = fb * (a + 16.85) * float(RoundToCeil(fb)); + if (fa == 0.0) + { + fa = 5.0; + } + fa = fc + (float(28)/fa) - RoundToFloor(fc); + } + StopProfiling(g_Prof); + PrintToServer("float benchmark: %f seconds", GetProfilerTime(g_Prof)); +} + +#define KEY1 "LVWANBAGVXSXUGB" +#define KEY2 "IDYCVNWEOWNND" +#define KEY3 "UZWTRNHY" +#define KEY4 "EPRHAFCIUOIG" +#define KEY5 "RMZCVWIEY" +#define KEY6 "ZHPU" + +StringBench() +{ + new i = STRING_FMT_LOOPS; + new String:buffer[255]; + + StartProfiling(g_Prof); + new end + while (i--) + { + end = 0; + Format(buffer, sizeof(buffer), "%d", i); + Format(buffer, sizeof(buffer), "%d %s %d %f %d %-3.4s %s", i, "gaben", 30, 10.0, 20, "hello", "What a gaben"); + end = Format(buffer, sizeof(buffer), "Well, that's just %-17.18s!", "what. this isn't a valid string! wait it is"); + end += Format(buffer[end], sizeof(buffer)-end, "There are %d in this %d", i, end); + end += Format(buffer[end], sizeof(buffer)-end, "There are %d in this %d", i, end); + } + StopProfiling(g_Prof); + PrintToServer("format() benchmark: %f seconds", GetProfilerTime(g_Prof)); + + StartProfiling(g_Prof); + i = STRING_ML_LOOPS; + new String:fmtbuf[2048]; /* don't change to decl, amxmodx doesn't use it */ + while (i--) + { + Format(fmtbuf, 2047, "%T %T %d %s %f %T", KEY1, LANG_SERVER, KEY2, LANG_SERVER, 50, "what the", 50.0, KEY3, LANG_SERVER); + Format(fmtbuf, 2047, "%s %T %s %T %T", "gaben", KEY4, LANG_SERVER, "what TIME is it", KEY5, LANG_SERVER, KEY6, LANG_SERVER); + } + StopProfiling(g_Prof); + PrintToServer("ml benchmark: %f seconds", GetProfilerTime(g_Prof)); + + StartProfiling(g_Prof); + i = STRING_OP_LOOPS; + while (i--) + { + StringToInt(fmtbuf) + } + StopProfiling(g_Prof); + PrintToServer("str benchmark: %f seconds", GetProfilerTime(g_Prof)); + + StartProfiling(g_Prof); + i = STRING_RPLC_LOOPS; + while (i--) + { + strcopy(fmtbuf, 2047, "This is a test string for you."); + ReplaceString(fmtbuf, sizeof(fmtbuf), " ", "ASDF") + ReplaceString(fmtbuf, sizeof(fmtbuf), "SDF", "") + ReplaceString(fmtbuf, sizeof(fmtbuf), "string", "gnirts") + } + StopProfiling(g_Prof); + PrintToServer("replace benchmark: %f seconds", GetProfilerTime(g_Prof)); +}