WIP: performance improvement: copy-on-write for flat edge pairs

This commit is contained in:
Matthias Koefferlein 2020-12-27 18:45:10 +01:00
parent 4ec00fb129
commit cc58d7d8ee
7 changed files with 547 additions and 29 deletions

View File

@ -32,7 +32,7 @@ namespace db
// FlatEdgePairs implementation
FlatEdgePairs::FlatEdgePairs ()
: AsIfFlatEdgePairs (), m_edge_pairs (false)
: AsIfFlatEdgePairs (), mp_edge_pairs (new db::Shapes (false))
{
// .. nothing yet ..
}
@ -43,13 +43,13 @@ FlatEdgePairs::~FlatEdgePairs ()
}
FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other)
: AsIfFlatEdgePairs (other), m_edge_pairs (false)
: AsIfFlatEdgePairs (other), mp_edge_pairs (other.mp_edge_pairs)
{
m_edge_pairs = other.m_edge_pairs;
// .. nothing yet ..
}
FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs)
: AsIfFlatEdgePairs (), m_edge_pairs (edge_pairs)
: AsIfFlatEdgePairs (), mp_edge_pairs (new db::Shapes (edge_pairs))
{
// .. nothing yet ..
}
@ -61,56 +61,58 @@ void FlatEdgePairs::invalidate_cache ()
void FlatEdgePairs::reserve (size_t n)
{
m_edge_pairs.reserve (db::EdgePair::tag (), n);
mp_edge_pairs->reserve (db::EdgePair::tag (), n);
}
EdgePairsIteratorDelegate *FlatEdgePairs::begin () const
{
return new FlatEdgePairsIterator (&m_edge_pairs);
return new FlatEdgePairsIterator (mp_edge_pairs.get_const ());
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatEdgePairs::begin_iter () const
{
return std::make_pair (db::RecursiveShapeIterator (m_edge_pairs), db::ICplxTrans ());
return std::make_pair (db::RecursiveShapeIterator (*mp_edge_pairs), db::ICplxTrans ());
}
bool FlatEdgePairs::empty () const
{
return m_edge_pairs.empty ();
return mp_edge_pairs->empty ();
}
size_t FlatEdgePairs::count () const
{
return m_edge_pairs.size ();
return mp_edge_pairs->size ();
}
size_t FlatEdgePairs::hier_count () const
{
return m_edge_pairs.size ();
return mp_edge_pairs->size ();
}
Box FlatEdgePairs::compute_bbox () const
{
m_edge_pairs.update_bbox ();
return m_edge_pairs.bbox ();
mp_edge_pairs->update_bbox ();
return mp_edge_pairs->bbox ();
}
EdgePairsDelegate *
FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
{
edge_pair_iterator_type pw = m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin ();
db::Shapes &ep = *mp_edge_pairs;
edge_pair_iterator_type pw = ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin ();
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
if (filter.selected (*p)) {
if (pw == m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ()) {
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().insert (*p);
pw = m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ();
if (pw == ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ()) {
ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().insert (*p);
pw = ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ();
} else {
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (pw++, *p);
ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (pw++, *p);
}
}
}
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().erase (pw, m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().erase (pw, ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
return this;
}
@ -147,22 +149,24 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
{
invalidate_cache ();
db::Shapes &ep = *mp_edge_pairs;
FlatEdgePairs *other_flat = dynamic_cast<FlatEdgePairs *> (other.delegate ());
if (other_flat) {
m_edge_pairs.insert (other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (), other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
ep.insert (other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (), other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
} else {
size_t n = m_edge_pairs.size ();
size_t n = ep.size ();
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
++n;
}
m_edge_pairs.reserve (db::EdgePair::tag (), n);
ep.reserve (db::EdgePair::tag (), n);
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
m_edge_pairs.insert (*p);
ep.insert (*p);
}
}
@ -172,7 +176,7 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
const db::EdgePair *FlatEdgePairs::nth (size_t n) const
{
return n < m_edge_pairs.size () ? &m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0;
return n < mp_edge_pairs->size () ? &mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0;
}
bool FlatEdgePairs::has_valid_edge_pairs () const
@ -197,13 +201,13 @@ FlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into
void
FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
{
layout->cell (into_cell).shapes (into_layer).insert (m_edge_pairs);
layout->cell (into_cell).shapes (into_layer).insert (*mp_edge_pairs);
}
void
FlatEdgePairs::insert (const db::EdgePair &ep)
{
m_edge_pairs.insert (ep);
mp_edge_pairs->insert (ep);
invalidate_cache ();
}

View File

@ -29,6 +29,7 @@
#include "dbAsIfFlatEdgePairs.h"
#include "dbShapes.h"
#include "dbGenericShapeIterator.h"
#include "tlCopyOnWrite.h"
namespace db {
@ -109,14 +110,16 @@ public:
void transform (const Trans &trans)
{
if (! trans.is_unity ()) {
for (edge_pair_iterator_type p = m_edge_pairs.template get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (); p != m_edge_pairs.template get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); ++p) {
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
db::Shapes &ep = *mp_edge_pairs;
for (edge_pair_iterator_type p = ep.template get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (); p != ep.template get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); ++p) {
ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
}
invalidate_cache ();
}
}
db::Shapes &raw_edge_pairs () { return m_edge_pairs; }
db::Shapes &raw_edge_pairs () { return *mp_edge_pairs; }
const db::Shapes &raw_edge_pairs () const { return *mp_edge_pairs; }
protected:
virtual Box compute_bbox () const;
@ -127,7 +130,7 @@ private:
FlatEdgePairs &operator= (const FlatEdgePairs &other);
mutable db::Shapes m_edge_pairs;
mutable tl::copy_on_write_ptr<db::Shapes> mp_edge_pairs;
};
}

View File

@ -11,6 +11,7 @@ FORMS =
SOURCES = \
tlAssert.cc \
tlClassRegistry.cc \
tlCopyOnWrite.cc \
tlDataMapping.cc \
tlDeflate.cc \
tlException.cc \
@ -54,6 +55,7 @@ HEADERS = \
tlAlgorithm.h \
tlAssert.h \
tlClassRegistry.h \
tlCopyOnWrite.h \
tlDataMapping.h \
tlDeflate.h \
tlException.h \

View File

@ -0,0 +1,31 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlCopyOnWrite.h"
namespace tl
{
tl::Mutex tl::CopyOnWritePtrBase::ms_lock;
}

264
src/tl/tl/tlCopyOnWrite.h Normal file
View File

@ -0,0 +1,264 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_tlCopyOnWrite
#define HDR_tlCopyOnWrite
#include "tlCommon.h"
#include "tlThreads.h"
namespace tl
{
/**
* @brief A copy duplicator (see copy_on_write_ptr below)
*/
template <class X>
struct copy_duplicator
{
X *operator() (const X &o)
{
return new X (o);
}
};
/**
* @brief A clone duplicator (see copy_on_write_ptr below)
*/
template <class X>
struct clone_duplicator
{
X *operator() (const X &o)
{
return o.clone ();
}
};
/**
* @brief A base class for the copy-on-write shared pointers
*/
class TL_PUBLIC CopyOnWritePtrBase
{
protected:
static tl::Mutex ms_lock;
};
/**
* @brief The holder object: keeps the actual reference of the object
*/
template <class X>
class copy_on_write_holder
{
public:
copy_on_write_holder (X *x)
: m_ref_count (1), mp_x (x)
{ }
~copy_on_write_holder ()
{
delete mp_x;
mp_x = 0;
}
X *x ()
{
return mp_x;
}
int ref_count () const { return m_ref_count; };
int dec_ref () { return --m_ref_count; }
void inc_ref () { ++m_ref_count; }
private:
int m_ref_count;
X *mp_x;
};
/**
* @brief Supplies a copy-on-write shared pointer scheme
*
* The idea is to provide a smart, "unique" pointer providing a copy constructor and assignment, but
* sharing the object as long as the object is not written.
*
* Write access is assumed as soon as the non-const pointer is retrieved.
*
* In order to duplicate the object, a "duplicator" needs to be defined. By default, the
* object is duplicated using the copy constructor ("copy_duplicator"). An alternative implementation
* is provided through the "clone_duplicator", which assumes a "clone" method to supply the duplicated
* object.
*/
template <class X, class Dup = copy_duplicator<X> >
class copy_on_write_ptr
: public CopyOnWritePtrBase
{
public:
typedef X value_type;
copy_on_write_ptr ()
: mp_holder (0)
{ }
copy_on_write_ptr (X *x)
: mp_holder (x ? new copy_on_write_holder<X> (x) : 0)
{ }
explicit copy_on_write_ptr (const copy_on_write_ptr<X, Dup> &other)
: mp_holder (other.mp_holder)
{
aquire ();
}
copy_on_write_ptr &operator= (const copy_on_write_ptr<X, Dup> &other)
{
if (this != &other) {
release ();
mp_holder = other.mp_holder;
aquire ();
}
return *this;
}
~copy_on_write_ptr ()
{
release ();
}
/**
* @brief Gets a writable object
* This is when we will create a new copy if the object is shared.
*/
X *get_non_const ()
{
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
if (mp_holder->ref_count () > 1) {
X *x = mp_holder->x ();
mp_holder->dec_ref ();
mp_holder = new copy_on_write_holder<X> (Dup () (*x));
}
return mp_holder->x ();
} else {
return 0;
}
}
/**
* @brief Gets the const pointer
* No copy will be created.
*/
const X *get_const () const
{
if (mp_holder) {
return mp_holder->x ();
} else {
return 0;
}
}
/**
* @brief Sets the pointer
*/
void reset (X *x)
{
release ();
if (x) {
mp_holder = new copy_on_write_holder<X> (x);
}
}
/**
* @brief Gets the non-const pointer
* This is when we will create a new copy if the object is shared.
*/
X *operator-> ()
{
return get_non_const ();
}
/**
* @brief Gets the const pointer
* No copy will be created.
*/
const X *operator-> () const
{
return get_const ();
}
/**
* @brief Gets the non-const reference
* This is when we will create a new copy if the object is shared.
*/
X &operator* ()
{
return *get_non_const ();
}
/**
* @brief Gets the const reference
* No copy will be created.
*/
const X &operator* () const
{
return *get_const ();
}
/**
* @brief Debugging and testing: gets the reference count
*/
int ref_count () const
{
int rc = 0;
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
rc = mp_holder->ref_count ();
}
return rc;
}
private:
X *mp_x;
copy_on_write_holder<X> *mp_holder;
void release ()
{
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
if (mp_holder->dec_ref () <= 0) {
delete mp_holder;
}
mp_holder = 0;
}
}
void aquire ()
{
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
mp_holder->inc_ref ();
}
}
};
}
#endif

View File

@ -0,0 +1,213 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlCopyOnWrite.h"
#include "tlUnitTest.h"
// This namespace separates the test structs from other objects
namespace copy_on_write_tests
{
static int x_instances = 0;
class X
{
public:
X () { ++x_instances; }
virtual ~X () { --x_instances; }
virtual const char *name () const = 0;
virtual X *clone () const = 0;
};
class Y : public X
{
public:
virtual const char *name () const { return "Y"; }
virtual X *clone () const { return new Y (); }
};
class Z : public X
{
public:
virtual const char *name () const { return "Z"; }
virtual X *clone () const { return new Z (); }
};
static int a_instances = 0;
class A
{
public:
A () { ++a_instances; }
A (const A &) { ++a_instances; }
~A () { --a_instances; }
const char *name () const { return "A"; }
};
}
TEST(1)
{
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr1, ptr2;
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
ptr1.reset (new copy_on_write_tests::A ());
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
ptr2 = ptr1;
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (copy_on_write_tests::a_instances, 2);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 1);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr2.get_const ()->name (), "A");
ptr1.reset (0);
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
ptr2.reset (0);
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
}
TEST(2)
{
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr1;
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
ptr1.reset (new copy_on_write_tests::A ());
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr2 (ptr1);
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 1);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr2.get_const ()->name (), "A");
}
TEST(3)
{
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr1;
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
ptr1.reset (new copy_on_write_tests::A ());
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr2 (ptr1);
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
ptr2.reset (0);
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 0);
EXPECT_EQ (ptr1.get_const () == 0, false);
EXPECT_EQ (ptr2.get_const () == 0, true);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
ptr1.reset (0);
EXPECT_EQ (ptr1.ref_count (), 0);
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
}
TEST(4)
{
tl::copy_on_write_ptr<copy_on_write_tests::X, tl::clone_duplicator<copy_on_write_tests::X> > ptr1, ptr2;
EXPECT_EQ (copy_on_write_tests::x_instances, 0);
ptr1.reset (new copy_on_write_tests::Y ());
EXPECT_EQ (copy_on_write_tests::x_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "Y");
EXPECT_EQ (ptr1.get_non_const ()->name (), "Y");
EXPECT_EQ (ptr1.ref_count (), 1);
ptr2 = ptr1;
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "Y");
EXPECT_EQ (ptr1.get_non_const ()->name (), "Y");
EXPECT_EQ (copy_on_write_tests::x_instances, 2);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 1);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
EXPECT_EQ (ptr1.get_const ()->name (), "Y");
EXPECT_EQ (ptr2.get_const ()->name (), "Y");
ptr1.reset (0);
EXPECT_EQ (copy_on_write_tests::x_instances, 1);
ptr2.reset (new copy_on_write_tests::Z ());
EXPECT_EQ (copy_on_write_tests::x_instances, 1);
EXPECT_EQ (ptr2.get_const ()->name (), "Z");
ptr2.reset (0);
EXPECT_EQ (copy_on_write_tests::x_instances, 0);
}

View File

@ -10,6 +10,7 @@ SOURCES = \
tlAlgorithm.cc \
tlClassRegistry.cc \
tlCommandLineParser.cc \
tlCopyOnWriteTests.cc \
tlDataMapping.cc \
tlDeflate.cc \
tlEvents.cc \