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));
+}