Attempt to achieve reproducibility between MSVC and gcc

Applies to dbHierProcessor.cc:

The issue was related to std::unordered_set/map which
(as the name says) is not ordered. The output of the
boolean core computation step is currently dependent
on the order (it's single pass), hence the order of
the contexts matters.

Using ordered sets where possible and explicit
sorting might help.
This commit is contained in:
Matthias Koefferlein 2019-02-02 22:43:42 +01:00
parent 5daf34ed76
commit 99f111fe01
7 changed files with 77 additions and 37 deletions

View File

@ -404,6 +404,22 @@ namespace std
return hf;
}
};
/**
* @brief Generic hash for an ordered set
*/
template <class T>
struct hash<std::set<T> >
{
size_t operator() (const std::set<T> &o) const
{
size_t hf = 0;
for (typename std::set<T>::const_iterator i = o.begin (); i != o.end (); ++i) {
hf = hfunc (hf, std::hash <T> () (*i));
}
return hf;
}
};
}
#endif

View File

@ -242,14 +242,14 @@ LocalProcessorCellContexts::LocalProcessorCellContexts (const db::Cell *intruder
}
db::LocalProcessorCellContext *
LocalProcessorCellContexts::find_context (const key_type &intruders)
LocalProcessorCellContexts::find_context (const context_key_type &intruders)
{
std::unordered_map<key_type, db::LocalProcessorCellContext>::iterator c = m_contexts.find (intruders);
std::unordered_map<context_key_type, db::LocalProcessorCellContext>::iterator c = m_contexts.find (intruders);
return c != m_contexts.end () ? &c->second : 0;
}
db::LocalProcessorCellContext *
LocalProcessorCellContexts::create (const key_type &intruders)
LocalProcessorCellContexts::create (const context_key_type &intruders)
{
return &m_contexts[intruders];
}
@ -290,6 +290,18 @@ subtract (std::unordered_set<db::PolygonRef> &res, const std::unordered_set<db::
ep.process (pg, op);
}
namespace {
struct context_sorter
{
bool operator () (const std::pair<const LocalProcessorCellContexts::context_key_type *, db::LocalProcessorCellContext *> &a, const std::pair<const LocalProcessorCellContexts::context_key_type *, db::LocalProcessorCellContext *> &b)
{
return *a.first < *b.first;
}
};
}
void
LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc)
{
@ -300,7 +312,19 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte
int index = 0;
int total = int (m_contexts.size ());
for (std::unordered_map<key_type, db::LocalProcessorCellContext>::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) {
// NOTE: we use the ordering provided by key_type::operator< rather than the unordered map to achieve
// reproducability across different platforms. unordered_map is faster, but for processing them,
// strict ordering is a more robust choice.
std::vector<std::pair<const context_key_type *, db::LocalProcessorCellContext *> > sorted_contexts;
sorted_contexts.reserve (m_contexts.size ());
for (std::unordered_map<context_key_type, db::LocalProcessorCellContext>::iterator c = m_contexts.begin (); c != m_contexts.end (); ++c) {
sorted_contexts.push_back (std::make_pair (&c->first, &c->second));
}
std::sort (sorted_contexts.begin (), sorted_contexts.end (), context_sorter ());
for (std::vector<std::pair<const context_key_type *, db::LocalProcessorCellContext *> >::const_iterator c = sorted_contexts.begin (); c != sorted_contexts.end (); ++c) {
++index;
@ -311,31 +335,31 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte
if (first) {
{
tl::MutexLocker locker (&c->second.lock ());
common = c->second.propagated ();
tl::MutexLocker locker (&c->second->lock ());
common = c->second->propagated ();
}
CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell)
proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, common);
proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, common);
first = false;
} else {
std::unordered_set<db::PolygonRef> res;
{
tl::MutexLocker locker (&c->second.lock ());
res = c->second.propagated ();
tl::MutexLocker locker (&c->second->lock ());
res = c->second->propagated ();
}
{
CRONOLOGY_COMPUTE_BRACKET(event_compute_local_cell)
proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, c->first, res);
proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res);
}
if (common.empty ()) {
CRONOLOGY_COMPUTE_BRACKET(event_propagate)
c->second.propagate (res);
c->second->propagate (res);
} else if (res != common) {
@ -354,8 +378,8 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte
if (! lost.empty ()) {
subtract (common, lost, cell->layout (), proc->max_vertex_count (), proc->area_ratio ());
for (std::unordered_map<key_type, db::LocalProcessorCellContext>::iterator cc = m_contexts.begin (); cc != c; ++cc) {
cc->second.propagate (lost);
for (std::vector<std::pair<const context_key_type *, db::LocalProcessorCellContext *> >::const_iterator cc = sorted_contexts.begin (); cc != c; ++cc) {
cc->second->propagate (lost);
}
}
@ -373,7 +397,7 @@ LocalProcessorCellContexts::compute_results (const LocalProcessorContexts &conte
subtract (gained, common, cell->layout (), proc->max_vertex_count (), proc->area_ratio ());
if (! gained.empty ()) {
c->second.propagate (gained);
c->second->propagate (gained);
}
}
@ -740,7 +764,7 @@ private:
// ---------------------------------------------------------------------------------------------
// LocalProcessorContextComputationTask implementation
LocalProcessorContextComputationTask::LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > &intruders, db::Coord dist)
LocalProcessorContextComputationTask::LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist)
: tl::Task (),
mp_proc (proc), mp_contexts (&contexts), mp_parent_context (parent_context),
mp_subject_parent (subject_parent), mp_subject_cell (subject_cell), m_subject_cell_inst (subject_cell_inst),
@ -849,7 +873,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts, const L
contexts.set_intruder_layer (intruder_layer);
contexts.set_subject_layer (subject_layer);
std::pair<std::unordered_set<db::CellInstArray>, std::unordered_set<db::PolygonRef> > intruders;
LocalProcessorCellContexts::context_key_type intruders;
issue_compute_contexts (contexts, 0, 0, mp_subject_top, db::ICplxTrans (), mp_intruder_top, intruders, op->dist ());
if (mp_cc_job.get ()) {
@ -869,7 +893,7 @@ void LocalProcessor::issue_compute_contexts (LocalProcessorContexts &contexts,
db::Cell *subject_cell,
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > &intruders,
LocalProcessorCellContexts::context_key_type &intruders,
db::Coord dist) const
{
bool is_small_job = subject_cell->begin ().at_end ();
@ -887,7 +911,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts,
db::Cell *subject_cell,
const db::ICplxTrans &subject_cell_inst,
const db::Cell *intruder_cell,
const std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > &intruders,
const LocalProcessorCellContexts::context_key_type &intruders,
db::Coord dist) const
{
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts)
@ -1008,7 +1032,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts,
}
for (std::unordered_set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
for (std::set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++id);
}
@ -1028,7 +1052,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts,
}
}
for (std::unordered_set<db::PolygonRef>::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) {
for (std::set<db::PolygonRef>::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) {
scanner.insert2 (i.operator-> (), 0);
}
@ -1053,7 +1077,7 @@ void LocalProcessor::compute_contexts (LocalProcessorContexts &contexts,
if (! nbox.empty ()) {
std::pair<std::unordered_set<db::CellInstArray>, std::unordered_set<db::PolygonRef> > intruders_below;
LocalProcessorCellContexts::context_key_type intruders_below;
db::shape_reference_translator_with_trans<db::PolygonRef, db::ICplxTrans> rt (mp_subject_layout, tni);
@ -1173,7 +1197,7 @@ LocalProcessor::compute_results (LocalProcessorContexts &contexts, const LocalOp
}
void
LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const db::LocalOperation *op, const std::pair<std::unordered_set<CellInstArray>, std::unordered_set<db::PolygonRef> > &intruders, std::unordered_set<db::PolygonRef> &result) const
LocalProcessor::compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const LocalProcessorCellContexts::context_key_type &intruders, std::unordered_set<db::PolygonRef> &result) const
{
const db::Shapes *subject_shapes = &subject_cell->shapes (contexts.subject_layer ());
@ -1221,7 +1245,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::
}
// TODO: TODO: can we confine this search to the subject's (sized) bounding box?
for (std::unordered_set<db::PolygonRef>::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) {
for (std::set<db::PolygonRef>::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) {
scanner.insert (i.operator-> (), interactions.next_id ());
}
@ -1239,7 +1263,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::
}
// TODO: can we confine this search to the subject's (sized) bounding box?
for (std::unordered_set<db::PolygonRef>::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) {
for (std::set<db::PolygonRef>::const_iterator i = intruders.second.begin (); i != intruders.second.end (); ++i) {
scanner.insert2 (i.operator-> (), interactions.next_id ());
}
@ -1284,7 +1308,7 @@ LocalProcessor::compute_local_cell (const LocalProcessorContexts &contexts, db::
}
// TODO: can we confine this search to the subject's (sized) bounding box?
for (std::unordered_set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
for (std::set<db::CellInstArray>::const_iterator i = intruders.first.begin (); i != intruders.first.end (); ++i) {
if (! inst_bci (*i).empty ()) {
scanner.insert2 (i.operator-> (), ++inst_id);
}

View File

@ -152,15 +152,15 @@ private:
class DB_PUBLIC LocalProcessorCellContexts
{
public:
typedef std::pair<std::unordered_set<CellInstArray>, std::unordered_set<db::PolygonRef> > key_type;
typedef std::unordered_map<key_type, db::LocalProcessorCellContext> map_type;
typedef map_type::const_iterator iterator;
typedef std::pair<std::set<CellInstArray>, std::set<db::PolygonRef> > context_key_type;
typedef std::unordered_map<context_key_type, db::LocalProcessorCellContext> context_map_type;
typedef context_map_type::const_iterator iterator;
LocalProcessorCellContexts ();
LocalProcessorCellContexts (const db::Cell *intruder_cell);
db::LocalProcessorCellContext *find_context (const key_type &intruders);
db::LocalProcessorCellContext *create (const key_type &intruders);
db::LocalProcessorCellContext *find_context (const context_key_type &intruders);
db::LocalProcessorCellContext *create (const context_key_type &intruders);
void compute_results (const LocalProcessorContexts &contexts, db::Cell *cell, const LocalOperation *op, unsigned int output_layer, const LocalProcessor *proc);
iterator begin () const
@ -175,7 +175,7 @@ public:
private:
const db::Cell *mp_intruder_cell;
std::unordered_map<key_type, db::LocalProcessorCellContext> m_contexts;
std::unordered_map<context_key_type, db::LocalProcessorCellContext> m_contexts;
};
class DB_PUBLIC LocalProcessorContexts
@ -260,7 +260,7 @@ class DB_PUBLIC LocalProcessorContextComputationTask
: public tl::Task
{
public:
LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > &intruders, db::Coord dist);
LocalProcessorContextComputationTask (const LocalProcessor *proc, LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist);
void perform ();
private:
@ -271,7 +271,7 @@ private:
db::Cell *mp_subject_cell;
db::ICplxTrans m_subject_cell_inst;
const db::Cell *mp_intruder_cell;
std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > m_intruders;
LocalProcessorCellContexts::context_key_type m_intruders;
db::Coord m_dist;
};
@ -382,11 +382,11 @@ private:
mutable std::auto_ptr<tl::Job<LocalProcessorContextComputationWorker> > mp_cc_job;
std::string description (const LocalOperation *op) const;
void compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > &intruders, db::Coord dist) const;
void do_compute_contexts (db::LocalProcessorCellContext *cell_context, const db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > &intruders, db::Coord dist) const;
void issue_compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, std::pair<std::unordered_set<CellInstArray>, std::unordered_set<PolygonRef> > &intruders, db::Coord dist) const;
void compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const;
void do_compute_contexts (db::LocalProcessorCellContext *cell_context, const db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const;
void issue_compute_contexts (db::LocalProcessorContexts &contexts, db::LocalProcessorCellContext *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, LocalProcessorCellContexts::context_key_type &intruders, db::Coord dist) const;
void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set<db::PolygonRef> &result) const;
void compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const std::pair<std::unordered_set<CellInstArray>, std::unordered_set<db::PolygonRef> > &intruders, std::unordered_set<db::PolygonRef> &result) const;
void compute_local_cell (const db::LocalProcessorContexts &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const LocalOperation *op, const LocalProcessorCellContexts::context_key_type &intruders, std::unordered_set<db::PolygonRef> &result) const;
};
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/algo/hlp6.oas vendored

Binary file not shown.