WIP: locking of netlist for better performance.

This commit is contained in:
Matthias Koefferlein 2018-12-29 01:04:48 +01:00
parent d57ede441c
commit 54adb84e27
3 changed files with 88 additions and 6 deletions

View File

@ -1129,13 +1129,13 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const
// Netlist class implementation
Netlist::Netlist ()
: m_valid_topology (false)
: m_valid_topology (false), m_lock_count (0)
{
m_circuits.changed ().add (this, &Netlist::invalidate_topology);
}
Netlist::Netlist (const Netlist &other)
: m_valid_topology (false)
: m_valid_topology (false), m_lock_count (0)
{
operator= (other);
m_circuits.changed ().add (this, &Netlist::invalidate_topology);
@ -1173,11 +1173,16 @@ Netlist &Netlist::operator= (const Netlist &other)
void Netlist::invalidate_topology ()
{
if (m_valid_topology) {
m_valid_topology = false;
m_top_circuits = 0;
m_top_down_circuits.clear ();
m_child_circuits.clear ();
m_parent_circuits.clear ();
if (m_lock_count == 0) {
m_top_circuits = 0;
m_top_down_circuits.clear ();
m_child_circuits.clear ();
m_parent_circuits.clear ();
}
}
}
@ -1195,6 +1200,8 @@ void Netlist::validate_topology ()
{
if (m_valid_topology) {
return;
} else if (m_lock_count > 0) {
return;
}
m_child_circuits.clear ();
@ -1295,6 +1302,21 @@ void Netlist::validate_topology ()
m_valid_topology = true;
}
void Netlist::lock ()
{
if (m_lock_count == 0) {
validate_topology ();
}
++m_lock_count;
}
void Netlist::unlock ()
{
if (m_lock_count > 0) {
--m_lock_count;
}
}
const tl::vector<Circuit *> &Netlist::child_circuits (Circuit *circuit)
{
if (! m_valid_topology) {

View File

@ -1806,6 +1806,30 @@ public:
*/
void clear ();
/**
* @brief Starts a sequence of operations during which topology updates are not desired
*
* If the hierarchy is modified, the topology information (top-down order, children
* and parent information) may be recomputed frequently. This may cause performance issues
* and may not be desired.
*
* Calling this method will bring the netlist into a state in which updates on the
* list will not happen. Using "unlock" will end this state.
*
* "lock" and "unlock" are incremental and can be nested. Use "NetlistLocker" for safe locking
* and unlocking.
*
* Before lock, the state will be validated, so inside the locked operation, the topology
* information will be valid with respect to the initial state.
*/
void lock ();
/**
* @brief Ends a sequence of operations during which topology updates are not desired
* See "lock" for more details.
*/
void unlock ();
/**
* @brief Adds a circuit to this netlist
*
@ -1974,6 +1998,7 @@ private:
circuit_list m_circuits;
device_class_list m_device_classes;
bool m_valid_topology;
int m_lock_count;
tl::vector<Circuit *> m_top_down_circuits;
tl::vector<tl::vector<Circuit *> > m_child_circuits;
tl::vector<tl::vector<Circuit *> > m_parent_circuits;
@ -1986,6 +2011,31 @@ private:
const tl::vector<Circuit *> &parent_circuits (Circuit *circuit);
};
/**
* @brief A helper class using RAII for safe locking/unlocking
*/
class DB_PUBLIC NetlistLocker
{
public:
NetlistLocker (Netlist *netlist)
: mp_netlist (netlist)
{
if (mp_netlist.get ()) {
mp_netlist->lock ();
}
}
~NetlistLocker ()
{
if (mp_netlist.get ()) {
mp_netlist->unlock ();
}
}
private:
tl::weak_ptr<Netlist> mp_netlist;
};
}
#endif

View File

@ -902,9 +902,19 @@ TEST(12_NetlistTopology)
EXPECT_EQ (td2string (nl.get ()), "c1,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c1");
std::auto_ptr<db::NetlistLocker> locker (new db::NetlistLocker (nl.get ()));
db::Circuit *c3 = new db::Circuit ();
c3->set_name ("c3");
nl->add_circuit (c3);
// because we locked, it did not get updated:
EXPECT_EQ (nl->top_circuit_count (), size_t (2));
EXPECT_EQ (td2string (nl.get ()), "c1,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c1");
locker.reset (0);
// after removing the lock, it's updated
EXPECT_EQ (nl->top_circuit_count (), size_t (3));
EXPECT_EQ (td2string (nl.get ()), "c1,c2,c3");
EXPECT_EQ (bu2string (nl.get ()), "c3,c2,c1");