From bee33fd486b6eeb09926a781a29d0a0e7b278bfd Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 26 Aug 2012 14:42:36 +0400 Subject: [PATCH] Add a performance test for location caching in general refs. --- plugins/devel/CMakeLists.txt | 3 + plugins/devel/ref-index.cpp | 149 +++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 plugins/devel/ref-index.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 8274accfb..134d5cb67 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 000000000..686f6918b --- /dev/null +++ b/plugins/devel/ref-index.cpp @@ -0,0 +1,149 @@ +#include "Core.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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 +T get_from_global_id_vector(int32_t id, const std::vector &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 T *find_object(int32_t id, int32_t *cache); +template<> df::item *find_object(int32_t id, int32_t *cache) { + return get_from_global_id_vector(id, df::global::world->items.all, cache); +} +template<> df::unit *find_object(int32_t id, int32_t *cache) { + return get_from_global_id_vector(id, df::global::world->units.all, cache); +} + +template +struct CachedRef { + int32_t id; + int32_t cache; + CachedRef(int32_t id = -1) : id(id), cache(-1) {} + T *target() { return find_object(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(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(unit_id, 1+&unit_id); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(unit_hook, getUnit); + +command_result hook_refs(color_ostream &out, vector & 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 &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; +}