Add a performance test for location caching in general refs.
parent
3402a3cd5d
commit
bee33fd486
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue