summaryrefslogtreecommitdiff
path: root/plugins/devel
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-08-26 14:42:36 +0400
committerAlexander Gavrilov2012-08-26 14:43:14 +0400
commitbee33fd486b6eeb09926a781a29d0a0e7b278bfd (patch)
treecf85be9e9af9905663e111c64705b013554203b0 /plugins/devel
parent3402a3cd5d473d26c115145e19f4113db51e8435 (diff)
downloaddfhack-bee33fd486b6eeb09926a781a29d0a0e7b278bfd.tar.gz
dfhack-bee33fd486b6eeb09926a781a29d0a0e7b278bfd.tar.bz2
dfhack-bee33fd486b6eeb09926a781a29d0a0e7b278bfd.tar.xz
Add a performance test for location caching in general refs.
Diffstat (limited to 'plugins/devel')
-rw-r--r--plugins/devel/CMakeLists.txt3
-rw-r--r--plugins/devel/ref-index.cpp149
2 files changed, 152 insertions, 0 deletions
diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt
index 8274accf..134d5cb6 100644
--- a/plugins/devel/CMakeLists.txt
+++ b/plugins/devel/CMakeLists.txt
@@ -18,3 +18,6 @@ DFHACK_PLUGIN(stripcaged stripcaged.cpp)
DFHACK_PLUGIN(rprobe rprobe.cpp)
DFHACK_PLUGIN(nestboxes nestboxes.cpp)
DFHACK_PLUGIN(vshook vshook.cpp)
+IF(UNIX)
+DFHACK_PLUGIN(ref-index ref-index.cpp)
+ENDIF()
diff --git a/plugins/devel/ref-index.cpp b/plugins/devel/ref-index.cpp
new file mode 100644
index 00000000..686f6918
--- /dev/null
+++ b/plugins/devel/ref-index.cpp
@@ -0,0 +1,149 @@
+#include "Core.h"
+#include <Console.h>
+#include <Export.h>
+#include <PluginManager.h>
+#include <modules/Gui.h>
+#include <modules/Screen.h>
+#include <vector>
+#include <cstdio>
+#include <stack>
+#include <string>
+#include <cmath>
+
+#include <VTableInterpose.h>
+#include "df/item.h"
+#include "df/unit.h"
+#include "df/world.h"
+#include "df/general_ref_item.h"
+#include "df/general_ref_unit.h"
+
+using std::vector;
+using std::string;
+using std::stack;
+using namespace DFHack;
+
+using df::global::gps;
+
+DFHACK_PLUGIN("ref-index");
+
+#define global_id id
+
+template<class T>
+T get_from_global_id_vector(int32_t id, const std::vector<T> &vect, int32_t *cache)
+{
+ size_t size = vect.size();
+ int32_t start=0;
+ int32_t end=(int32_t)size-1;
+
+ // Check the cached location. If it is a match, this provides O(1) lookup.
+ // Otherwise it works like one binsearch iteration.
+ if (size_t(*cache) < size)
+ {
+ T cptr = vect[*cache];
+ if (cptr->global_id == id)
+ return cptr;
+ if (cptr->global_id < id)
+ start = *cache+1;
+ else
+ end = *cache-1;
+ }
+
+ // Regular binsearch. The end check provides O(1) caching for missing item.
+ if (start <= end && vect[end]->global_id >= id)
+ {
+ do {
+ int32_t mid=(start+end)>>1;
+
+ T cptr=vect[mid];
+ if(cptr->global_id==id)
+ {
+ *cache = mid;
+ return cptr;
+ }
+ else if(cptr->global_id>id)end=mid-1;
+ else start=mid+1;
+ } while(start<=end);
+ }
+
+ *cache = end+1;
+ return NULL;
+}
+
+template<class T> T *find_object(int32_t id, int32_t *cache);
+template<> df::item *find_object<df::item>(int32_t id, int32_t *cache) {
+ return get_from_global_id_vector(id, df::global::world->items.all, cache);
+}
+template<> df::unit *find_object<df::unit>(int32_t id, int32_t *cache) {
+ return get_from_global_id_vector(id, df::global::world->units.all, cache);
+}
+
+template<class T>
+struct CachedRef {
+ int32_t id;
+ int32_t cache;
+ CachedRef(int32_t id = -1) : id(id), cache(-1) {}
+ T *target() { return find_object<T>(id, &cache); }
+};
+
+#ifdef LINUX_BUILD
+struct item_hook : df::general_ref_item {
+ typedef df::general_ref_item interpose_base;
+
+ DEFINE_VMETHOD_INTERPOSE(df::item*, getItem, ())
+ {
+ // HUGE HACK: ASSUMES THERE ARE 4 USABLE BYTES AFTER THE OBJECT
+ // This actually is true with glibc allocator due to granularity.
+ return find_object<df::item>(item_id, 1+&item_id);
+ }
+};
+
+IMPLEMENT_VMETHOD_INTERPOSE(item_hook, getItem);
+
+struct unit_hook : df::general_ref_unit {
+ typedef df::general_ref_unit interpose_base;
+
+ DEFINE_VMETHOD_INTERPOSE(df::unit*, getUnit, ())
+ {
+ // HUGE HACK: ASSUMES THERE ARE 4 USABLE BYTES AFTER THE OBJECT
+ // This actually is true with glibc allocator due to granularity.
+ return find_object<df::unit>(unit_id, 1+&unit_id);
+ }
+};
+
+IMPLEMENT_VMETHOD_INTERPOSE(unit_hook, getUnit);
+
+command_result hook_refs(color_ostream &out, vector <string> & parameters)
+{
+ auto &hook = INTERPOSE_HOOK(item_hook, getItem);
+ if (hook.is_applied())
+ {
+ hook.remove();
+ INTERPOSE_HOOK(unit_hook, getUnit).remove();
+ }
+ else
+ {
+ hook.apply();
+ INTERPOSE_HOOK(unit_hook, getUnit).apply();
+ }
+
+ if (hook.is_applied())
+ out.print("Hook is applied.\n");
+ else
+ out.print("Hook is not applied.\n");
+ return CR_OK;
+}
+#endif
+
+DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
+{
+#ifdef LINUX_BUILD
+ commands.push_back(PluginCommand("hook-refs","Inject O(1) cached lookup into general refs.",hook_refs));
+#endif
+
+ return CR_OK;
+}
+
+DFhackCExport command_result plugin_shutdown ( color_ostream &out )
+{
+ return CR_OK;
+}