This commit is contained in:
Garry Boyer
2007-05-03 05:45:06 +00:00
parent b1ab722f41
commit 6fd1a1e0e4
4 changed files with 417 additions and 84 deletions
+71
View File
@@ -42,3 +42,74 @@ Array
- blocks have no alignment
- read blocks on first use
- explicit ranged writebacks
how to build tree
- build first (log p) levels in parallel
- first: assume block size >= p
- first block is not completely filled
- next: if block size < p
- second layer of blocks not completely filled
- this may be space-hungry if B^2 < N, but in that case, you're kind of
silly for using that many processors
- so overall
- block wastage: P blocks max (really B^(floor((log P)/(log B)))),
(N log P)/(B log B) percent
- fits in memory
- arrays are synced by region
- trees are private-write
- doesn't fit in memory
- first:
- ensure writable portion fits in memory, lock it, and private writes
- allocate cache that can exactly fit it?
- non-writable parts of tree are not locked
- DFS: fit entire query into RAM, fit useful part of reference into RAM
- RBFS: only one or logarithmic query nodes even needed in RAM at a time
- for best performance, all relevant references must fit
- okay for nearest neighbors (linear search space), bad for larger
search spaces
- generalized: okay for *any* algorithm that performs well
single-tree!!!!!!!!!
- later: any point in *not* making this assumption?
- breadth first?
- query-reference problems: use recursive breadth first
- global problems: no writes to trees necessary
how to execute
- operation by pattern
- in best conditions these are all O((Q+R)/B)
- DFS
- (QR)/(MB)
- tree
- query: lock into RAM, private writes
- reference: load via cache
- I/O worst-case: (4QR/(MB))
- data:
- make sure query fits in RAM
- reference: ranged
- RBFS
- (QR log B)/(B) without block-chunking
- tree
- query: one block at a time
- reference: via cache
- if reference set stays large, cache performance will be abysmal
- make sure references are sorted based on block order
- if it was N^2 time before I/O efficiency would be
- I/O rate: O(Q(R/B)(log B)), but it could have been
O((Q/M)(R/B))
- *sigh* still better than single-tree
- data: cached with ranges
- Block breadth-first
- (QR log B)/(MB)
- looks at pairs of blocks
- tree: only one query and one references needed at any time
- queue size: QR/B^2
- I/O worst-case: either QR/B^2 or (4QR/(MB))(log B)
- towards the bottom, we end up working on pairs of half-memory-sized
chunks
- we may choose number of points per leaf to be comparable to log B
- for simplicity, my first version simply won't support mu
(mu will just equal the root's delta); we will just target
gamma-based problems
+190 -81
View File
@@ -168,8 +168,10 @@ class RecursiveBreadthFirstDualTreeRunner {
Param param_;
ArrayList<QInfo> q_info_;
ArrayList<RInfo> r_info_;
QNode *q_root_;
RNode *r_root_;
const QNode *q_root_;
const RNode *r_root_;
ArrayList<QResult> q_result_;
GlobalResult global_result_;
public:
void Init(struct datanode *module,
@@ -187,11 +189,15 @@ class RecursiveBreadthFirstDualTreeRunner {
}
ArrayList<QInfo>& q_info() {
return q_info;
return q_info_;
}
ArrayList<RInfo>& r_info() {
return r_info;
return r_info_;
}
ArrayList<QResult>& q_result() {
return q_result_;
}
private:
@@ -203,20 +209,20 @@ class RecursiveBreadthFirstDualTreeRunner {
DEBUG_POISON_PTR(r_node);
}
RNode *r_node;
const RNode *r_node;
Delta delta;
};
private:
const Param *param_;
QNode *q_node_;
const QNode *q_node_;
ArrayList<Entry> list_;
MassResult q_mass_result_;
PostponedResult q_postponed_;
QMassResult q_mass_result_;
QPostponedResult q_postponed_;
GlobalResult *global_result_;
public:
void Init(QNode* q_node_in, const Param* param,
void Init(const QNode* q_node_in, const Param* param,
GlobalResult* global_result_in) {
param_ = param;
q_node_ = q_node_in;
@@ -230,7 +236,7 @@ class RecursiveBreadthFirstDualTreeRunner {
global_result_ = global_result_in;
}
void Init(QNode* q_node_in, const Queue& parent) {
void Init(const QNode* q_node_in, const Queue& parent) {
param_ = parent.param;
q_node_ = q_node_in;
@@ -244,14 +250,14 @@ class RecursiveBreadthFirstDualTreeRunner {
global_result_ = parent.global_result_;
}
void Add(RNode *r_node) {
void Add(const RNode *r_node) {
Entry *entry = list_.AddBack();
bool try_explore = Algorithm::ConsiderPairIntrinsic(
*param_, *q_node_, *r_node,
&entry->delta, &q_mass_result_, global_result_, &q_postponed_);
&entry->delta, global_result_, &q_postponed_);
if (try_explore) {
entry->r_node = r_node;
q_mass_result_.ApplyDelta(*param_, entry->delta);
} else {
list_.PopBack();
}
@@ -266,7 +272,7 @@ class RecursiveBreadthFirstDualTreeRunner {
return list_.size();
}
RNode* rnode(index_t i) const {
const RNode* rnode(index_t i) const {
return ;
}
@@ -275,92 +281,195 @@ class RecursiveBreadthFirstDualTreeRunner {
return list_[i].delta;
}
const MassResult& q_mass_result() const { return q_mass_result_; }
const QMassResult& q_mass_result() const { return q_mass_result_; }
const PostponedResult& q_postponed() const { return q_postponed_; }
const QPostponedResult& q_postponed() const { return q_postponed_; }
PostponedResult& q_postponed() { return q_postponed_; }
QPostponedResult& q_postponed() { return q_postponed_; }
};
void SplitQ(QNode *q_node, const Queue& list_old) {
if (q_node->is_leaf()) {
DoQLeafStuffWALDO_TODO(q_node, list_old);
return;
}
if (Algorithm::ConsiderQueryTermination(&param_, *q_node,
void RecursivelyApplyPostponed(
const QNode* q_node, const QPostponedResult& postponed_result);
bool TryTerminate(const QNode *q_node, const Queue& list_old) {
if (Algorithm::ConsiderQueryTermination(param_, *q_node,
list_old.q_mass_result(), *global_result_, &list_old.q_postponed())) {
RecursivelyApplyPostponed(q_node, list_old.q_postponed());
return;
}
Queue list_new[cardinality];
/* TODO: termination prunes can be checked here */
for (int c = 0; c < cardinality; c++) {
list_new[c].Init(q_node->child(i), list_old);
}
QPostponedResults postponed;
postponed.Init(*param_);
// We haven't done any exhaustive comparisons, we start with an empty.
for (index_t i = 0; i < list_old.size(); i++) {
RNode *r_node = list_old.rnode(i);
const Delta* delta = &list_old.delta(i);
if (likely(Algorithm::ConsiderPairExtrinsic(param_, *q_node, *r_node,
*delta, list_old.q_mass_result(), *global_result_, &postponed))) {
global_result_.UndoDelta(param_, *delta);
for (int c_q = 0; c_q < cardinality; c_q++) {
global_result_.ApplyDelta(param_, *delta);
list_new[c_q].Add(r_node);
}
}
}
/* recurse over query children */
for (int c = 0; c < cardinality; c++) {
list_new[c].q_postponed().ApplyPostponed(*param_, postponed);
list_new[c].Finish(param_);
SplitR(q_node->child(c), list_new[c]);
}
}
void SplitR(QNode *q_node, const ArrayList<Entry>& list_old) {
Queue list_new;
void FinishQ(const QNode *q_node, const Queue& list_old);
void SplitQ(const QNode *q_node, const Queue& list_old);
void SplitR(const QNode *q_node, const ArrayList<Entry>& list_old);
};
template<class GNP>
void RecursiveBreadthFirstDualTreeRunner<GNP>::RecursivelyApplyPostponed(
const QNode* q_node, const QPostponedResult& postponed_result) {
for (index_t i = q_node->begin(); i < q_node->end(); i++) {
q_results_[i].ApplyPostponedResult(param_, postponed_result, *q_node);
}
}
template<class GNP>
void RecursiveBreadthFirstDualTreeRunner<GNP>::SplitQ(
const QNode *q_node, const Queue& list_old) {
if (q_node->is_leaf()) {
RecurseOnLeaves(q_node, list_old);
return;
}
if (TryTerminate(q_node, list_old)) {
return;
}
Queue list_new[cardinality];
/* TODO: termination prunes can be checked here */
for (int c = 0; c < cardinality; c++) {
list_new[c].Init(q_node->child(i), list_old);
}
QPostponedResults postponed;
postponed.Init(param_);
// We haven't done any exhaustive comparisons, we start with an empty.
for (index_t i = 0; i < list_old.size(); i++) {
const RNode *r_node = list_old.rnode(i);
const Delta* delta = &list_old.delta(i);
if (Algorithm::ConsiderQueryTermination(&param_, *q_node,
list_old.q_mass_result(), *global_result_, &list_old.q_postponed())) {
RecursivelyApplyPostponed(q_node, list_old.q_postponed());
return;
if (likely(Algorithm::ConsiderPairExtrinsic(param_, *q_node, *r_node,
*delta, list_old.q_mass_result(), *global_result_, &postponed))) {
global_result_.UndoDelta(param_, *delta);
for (int c_q = 0; c_q < cardinality; c_q++) {
global_result_.ApplyDelta(param_, *delta);
list_new[c_q].Add(r_node);
}
}
}
/* recurse over query children */
for (int c = 0; c < cardinality; c++) {
list_new[c].q_postponed().ApplyPostponed(param_, postponed);
list_new[c].Finish(param_);
SplitR(q_node->child(c), list_new[c]);
}
}
template<class GNP>
void RecursiveBreadthFirstDualTreeRunner<GNP>::SplitR(
const QNode *q_node, const ArrayList<Entry>& list_old) {
Queue list_new;
if (TryTerminate(q_node, list_old)) {
return;
}
list_new.Init(q_node, list_old);
for (index_t i = 0; i < list_old.size(); i++) {
const RNode *r_node = list_old.rnode(i);
const Delta* delta = &list_old.delta(i);
list_new.Init(q_node, list_old);
for (index_t i = 0; i < list_old.size(); i++) {
RNode *r_node = list_old.rnode(i);
const Delta* delta = &list_old.delta(i);
if (likely(Algorithm::ConsiderPairExtrinsic(param_, *q_node, *r_node,
*delta, list_old.q_mass_result(), *global_result_,
&list_new.q_postponed()))) {
if (entry_old->r_node->is_leaf()) {
// TODO: We can collapse the mu's for these together
list_new.Add(r_node);
} else {
if (likely(Algorithm::ConsiderPairExtrinsic(param_, *q_node, *r_node,
*delta, list_old.q_mass_result(), *global_result_,
&list_new.q_postponed()))) {
global_result_.UndoDelta(*delta);
for (int c = 0; c < cardinality; c++) {
global_result_.ApplyDelta(*delta);
list_new.Add(node->child(i));
}
global_result_.UndoDelta(*delta);
for (int c = 0; c < cardinality; c++) {
global_result_.ApplyDelta(*delta);
list_new.Add(node->child(i));
}
}
}
}
};
SplitQ(q_node, list_new);
}
template<class GNP>
void RecursiveBreadthFirstDualTreeRunner<GNP>::EnumerateLeaves(
const QNode *q_node, const RNode *r_node,
const QMassResult& old_q_mass_result,
) {
DEBUG_ASSERT(q_node->is_leaf());
if (likely(Algorithm::ConsiderPairExtrinsic(param_, *q_node, *r_node,
*delta, old_q_mass_result(), *global_result_,
&list_new.q_postponed()))) {
if (r_node->is_leaf()) {
void Add(const RNode *r_node) {
Entry *entry = list_.AddBack();
bool try_explore = Algorithm::ConsiderPairIntrinsic(
*param_, *q_node_, *r_node,
&entry->delta, global_result_, &q_postponed_);
if (try_explore) {
entry->r_node = r_node;
q_mass_result_.ApplyDelta(*param_, entry->delta);
} else {
list_.PopBack();
}
}
node_list->AddBack(r_node);
} else {
global_result_.UndoDelta(*delta);
for (int c = 0; c < cardinality; c++) {
global_result_.ApplyDelta(*delta);
EnumerateLeaves(q_node, node->child(i));
}
}
}
}
template<class GNP>
void RecursiveBreadthFirstDualTreeRunner<GNP>::HandleQueryLeaf(
const QNode *q_node, const Queue& list_old) {
Queue list_new;
DEBUG_ASSERT(q->is_leaf());
check for termination
list_new.Init(q_node, list_old);
for (index_t i = 0; i < list_old.size(); i++) {
const RNode *r_node = list_old.rnode(i);
const Delta* delta = &list_old.delta(i);
EnumerateLeaves(q_node, r_node, list_old.q_mass_result(), &list_new);
}
MinHeap<double, Queue::Entry*> priority_queue;
for (index_t i = 0; i < list_new.size(); i++) {
const Queue::Entry* entry = list_new.entry(i);
double heuristic = Algorithm::Heuristic(param_, *q_node, *entry->r_node,
entry->delta, list_old.mass_result());
priority_queue.Put(-heuristic, entry);
}
ArrayList<const Queue::Entry*> entries;
ArrayList<QMassResult> mass_results;
const QMassResult* cur_mass_result = ;
mass_results.Init(new_list.size());
entries.Init(new_list.size());
for (index_t i = new_list.size(); i--;) {
entries[i] = priority_queue.Pop();
mass_results[i].Copy(*cur_mass_result);
cur_mass_result = &mass_results[i];
}
}
+75 -3
View File
@@ -47,17 +47,35 @@ class Tkde {
}
};
struct QInfo {};
struct BlankInfo {
template<typename Serializer>
void Serialize(Serializer *s) const {}
template<typename Deserializer>
void Deserialize(Deserializer *s) {}
};
struct RInfo {};
typedef BlankInfo QInfo;
typedef BlankInfo RInfo;
struct MomentInfo {
ALLOW_COPY(MomentInfo);
public:
Vector mass;
double sumsq;
index_t count;
template<typename Serializer>
void Serialize(Serializer *s) const {
mass->Serialize(s);
s->Put(sumsq);
s->Put(count);
}
template<typename Deserializer>
void Deserialize(Deserializer *s) {
mass->Deserialize(s);
s->Get(&sumsq);
s->Get(&count);
}
void Init(const TkdeParam& param) {
mass.Init(param.dim);
@@ -110,6 +128,15 @@ class Tkde {
struct TkdeStat {
MomentInfo moment_info;
template<typename Serializer>
void Serialize(Serializer *s) const {
moment_info->Serialize(s);
}
template<typename Deserializer>
void Deserialize(Deserializer *s) {
moment_info->Deserialize(s);
}
void InitZero(const TkdeParam& param) {
moment_info.Init(param);
@@ -147,6 +174,16 @@ class Tkde {
/** We pruned an entire part of the tree with a particular label. */
Label label;
template<typename Serializer>
void Serialize(Serializer *s) const {
moment_info.Serialize(s);
s->Put(label);
}
template<typename Deserializer>
void Deserialize(Deserializer *s) {
moment_info->Deserialize(s);
s->Get(&label);
}
void Init(const TkdeParam& param) {
moment_info.Init(param);
@@ -169,6 +206,15 @@ class Tkde {
struct TkdeDelta {
/** Density update to apply to children's bound. */
DRange d_density;
template<typename Serializer>
void Serialize(Serializer *s) const {
d_density.Serialize(s);
}
template<typename Deserializer>
void Deserialize(Deserializer *s) {
d_density.Deserialize(s);
}
void Init(const TkdeParam& param) {
d_density.Init(0, 0);
@@ -184,6 +230,17 @@ class Tkde {
double density;
Label label;
template<typename Serializer>
void Serialize(Serializer *s) const {
s->Put(density);
s->Put(label);
}
template<typename Deserializer>
void Deserialize(Deserializer *s) {
s->Get(&density);
s->Get(&label);
}
void Init(const TkdeParam& param,
const Vector& q_point, const QInfo& q_info,
const RNode& r_root) {
@@ -214,6 +271,10 @@ class Tkde {
};
class TkdeGlobalResult {
template<typename Serializer>
void Serialize(Serializer *s) const {}
template<typename Deserializer>
void Deserialize(Deserializer *s) {}
void Init(const TkdeParam& param) {}
void Accumulate(const TkdeParam& param,
const TkdeGlobalResult& other_global_result) {}
@@ -227,6 +288,17 @@ class Tkde {
DRange density;
Label label;
template<typename Serializer>
void Serialize(Serializer *s) const {
density.Serialize(s);
s->Put(label);
}
template<typename Deserializer>
void Deserialize(Deserializer *s) {
density.Deserialize(s);
s->Get(&label);
}
void Copy(const TkdeMassResult& other) {
density = other.density;
label = other.label;
+81
View File
@@ -0,0 +1,81 @@
remember 143
/- serialization of tkde
- array char abstraction
- array char mem abstraction
- array char disk abstraction
- array char local cache abstraction
- array abstraction (kind of done already)
- dfs
- how to resolve write conflicts
- a: bit array
- b: ranges (requires carefulness)
- c: block exclusivity, enforced
- d: do it manually
- c: block exclusivity, assumed
blocker.read_block(i).offset(i)
- question: in DFS how do we get fault tolerance?
XXX- option 1: rely on mu
- we have to know the state of mu
- impossible to guarantee using a cache
- we never know the state of the data
- option 2: nuke
- we can always nuke a region of the query tree
- also required for work stealing
final answer:
- bit-vector or pre-allocated dirty flags
- abstractions
- to make network/disk access easy we use block backends. but there
are two frontends to these:
- scanning frontend (cache size is single block)
- Scanner(blockdevice, begin_id, end_id)
- sequential access
- can we put lots of scanners in a single dude?
- we don't want to
- avoid having tiny files
- allow run-time decision of backends: use virtual functions
- Cache(blockdevice, begin_id, end_id)
- random-access read write
void treebuild(array) {
makesample(array, &sample)
tree <- maketree(sample)
assign a number 1 to B to each leaf of the tree
newarray <- memarray hosted at me
allocators <- new blockallocators[B]
links <- new int[B]
for elem in array:
assign i to elem
int address = allocators[i].allocate()
newarray[address].content <- memcpy(elem)
if links[i]:
newarray[links[i]].link <- address
links[i] = address
for i in range(B) in parallel:
treebuild(newarray, [])
finish postponed items (the work queue thing realizes this thread is
blocking and uses it to run one of the work
threads)
}
NetManager
- simulates java RPC
manager.register(id, networklistener)
manager.unregister(id)
manager.send(id, datasize, data)
manager.workqueue_crap_crap