From 580033fc2b69798c0ba4518fb309fa9bd3afbebb Mon Sep 17 00:00:00 2001 From: Mathieu Lacage Date: Thu, 5 Nov 2009 21:04:05 +0100 Subject: [PATCH] Optimize Object::GetObject. Introduce an array of aggregates and sort is by access frequency. --- src/core/object.cc | 246 +++++++++++++++++++++++++++------------------ src/core/object.h | 41 ++++++-- 2 files changed, 180 insertions(+), 107 deletions(-) diff --git a/src/core/object.cc b/src/core/object.cc index a05e470d5..2725ba6cf 100644 --- a/src/core/object.cc +++ b/src/core/object.cc @@ -28,6 +28,8 @@ #include "string.h" #include #include +#include +#include NS_LOG_COMPONENT_DEFINE ("Object"); @@ -40,28 +42,23 @@ namespace ns3 { NS_OBJECT_ENSURE_REGISTERED (Object); Object::AggregateIterator::AggregateIterator () - : m_first (0), + : m_object (0), m_current (0) {} bool Object::AggregateIterator::HasNext (void) const { - if (m_current != 0 && m_current->m_next != PeekPointer (m_first)) - { - return true; - } - return false; + return m_current < m_object->m_aggregates->n; } Ptr Object::AggregateIterator::Next (void) { - m_current = m_current->m_next; - return m_current; + return m_object->m_aggregates->buffer[m_current]; } -Object::AggregateIterator::AggregateIterator (Ptr first) - : m_first (first), - m_current (first) +Object::AggregateIterator::AggregateIterator (Ptr object) + : m_object (object), + m_current (0) {} @@ -85,18 +82,45 @@ Object::Object () : m_count (1), m_tid (Object::GetTypeId ()), m_disposed (false), - m_next (this) -{} + m_aggregates ((struct Aggregates *)malloc (sizeof (struct Aggregates))), + m_getObjectCount (0) +{ + m_aggregates->n = 1; + m_aggregates->buffer[0] = this; +} Object::~Object () { - m_next = 0; + // remove this object from the aggregate list + uint32_t n = m_aggregates->n; + for (uint32_t i = 0; i < n; i++) + { + Object *current = m_aggregates->buffer[i]; + if (current == this) + { + memmove (&m_aggregates->buffer[i], + &m_aggregates->buffer[i+1], + sizeof (Object *)*(m_aggregates->n - (i+1))); + m_aggregates->n--; + } + } + // finally, if all objects have been removed from the list, + // delete the aggregate list + if (m_aggregates->n == 0) + { + free (m_aggregates); + } + m_aggregates = 0; } Object::Object (const Object &o) : m_count (1), m_tid (o.m_tid), m_disposed (false), - m_next (this) -{} + m_aggregates ((struct Aggregates *)malloc (sizeof (struct Aggregates))), + m_getObjectCount (0) +{ + m_aggregates->n = 1; + m_aggregates->buffer[0] = this; +} uint32_t Object::GetReferenceCount (void) const { @@ -112,48 +136,58 @@ Ptr Object::DoGetObject (TypeId tid) const { NS_ASSERT (CheckLoose ()); - const Object *currentObject = this; - const Object *prevObject = 0; + + uint32_t n = m_aggregates->n; TypeId objectTid = Object::GetTypeId (); - do { - NS_ASSERT (currentObject != 0); - TypeId cur = currentObject->GetInstanceTypeId (); - while (cur != tid && cur != objectTid) - { - cur = cur.GetParent (); - } - if (cur == tid) - { - if (prevObject != 0) - { - // This is an attempt to 'cache' the result of this lookup. - // the idea is that if we perform a lookup for a TypdId on this object, - // we are likely to perform the same lookup later so, we re-order - // the circular linked-list of objects here by putting the object we - // just found at the head of the list. This optimization is - // _extremely_ effective in general. - const_cast(prevObject)->m_next = currentObject->m_next; - const_cast(currentObject)->m_next = m_next; - const_cast(this)->m_next = (Object*)currentObject; - } - return const_cast (currentObject); - } - prevObject = currentObject; - currentObject = currentObject->m_next; - } while (currentObject != this); + for (uint32_t i = 0; i < n; i++) + { + Object *current = m_aggregates->buffer[i]; + TypeId cur = current->GetInstanceTypeId (); + while (cur != tid && cur != objectTid) + { + cur = cur.GetParent (); + } + if (cur == tid) + { + // This is an attempt to 'cache' the result of this lookup. + // the idea is that if we perform a lookup for a TypeId on this object, + // we are likely to perform the same lookup later so, we make sure + // that the aggregate array is sorted by the number of accesses + // to each object. + + // first, increment the access count + current->m_getObjectCount++; + // then, update the sort + UpdateSortedArray (m_aggregates, i); + // finally, return the match + return const_cast (current); + } + } return 0; } void Object::Dispose (void) { - Object *current = this; - do { - NS_ASSERT (current != 0); - NS_ASSERT (!current->m_disposed); - current->DoDispose (); - current->m_disposed = true; - current = current->m_next; - } while (current != this); + uint32_t n = m_aggregates->n; + for (uint32_t i = 0; i < n; i++) + { + Object *current = m_aggregates->buffer[i]; + NS_ASSERT (!current->m_disposed); + current->DoDispose (); + current->m_disposed = true; + } +} +void +Object::UpdateSortedArray (struct Aggregates *aggregates, uint32_t j) const +{ + while (j > 0 && + aggregates->buffer[j]->m_getObjectCount > aggregates->buffer[j-1]->m_getObjectCount) + { + Object *tmp = aggregates->buffer[j-1]; + aggregates->buffer[j-1] = aggregates->buffer[j]; + aggregates->buffer[j] = tmp; + j--; + } } void Object::AggregateObject (Ptr o) @@ -171,20 +205,38 @@ Object::AggregateObject (Ptr o) } Object *other = PeekPointer (o); - Object *next = m_next; - m_next = other->m_next; - other->m_next = next; - NS_ASSERT (CheckLoose ()); - NS_ASSERT (o->CheckLoose ()); - // call NotifyNewAggregate in the listed chain - Object *currentObject = this; - do + // first create the new aggregate buffer. + uint32_t total = m_aggregates->n + other->m_aggregates->n; + struct Aggregates *aggregates = + (struct Aggregates *)malloc (sizeof(struct Aggregates)+(total-1)*sizeof(Object*)); + aggregates->n = total; + memcpy (&aggregates->buffer[0], + &m_aggregates->buffer[0], + m_aggregates->n*sizeof(Object*)); + // append the other aggregates in the new buffer + for (uint32_t i = 0; i < other->m_aggregates->n; i++) { - // the NotifyNewAggregate of the current object implementation - // should be called on the next object in the linked chain - currentObject->NotifyNewAggregate (); - currentObject = currentObject->m_next; - } while (currentObject != this); + aggregates->buffer[m_aggregates->n+i] = other->m_aggregates->buffer[i]; + UpdateSortedArray (aggregates, m_aggregates->n + i); + } + + // free both aggregate buffers + free (m_aggregates); + free (other->m_aggregates); + + // Then, assign that buffer to every object + uint32_t n = aggregates->n; + for (uint32_t i = 0; i < n; i++) + { + Object *current = aggregates->buffer[i]; + current->m_aggregates = aggregates; + } + // Finally, call NotifyNewAggregate in the listed chain + for (uint32_t i = 0; i < n; i++) + { + Object *current = m_aggregates->buffer[i]; + current->NotifyNewAggregate (); + } } /** * This function must be implemented in the stack that needs to notify @@ -233,14 +285,12 @@ bool Object::CheckLoose (void) const { uint32_t refcount = 0; - const Object *current = this; - do + uint32_t n = m_aggregates->n; + for (uint32_t i = 0; i < n; i++) { + Object *current = m_aggregates->buffer[i]; refcount += current->m_count; - current = current->m_next; } - while (current != this); - return (refcount > 0); } @@ -249,38 +299,38 @@ Object::MaybeDelete (void) const { // First, check if any of the attached // Object has a non-zero count. - const Object *current = this; - do { - NS_ASSERT (current != 0); - if (current->m_count != 0) - { - return; - } - current = current->m_next; - } while (current != this); + uint32_t n = m_aggregates->n; + for (uint32_t i = 0; i < n; i++) + { + Object *current = m_aggregates->buffer[i]; + if (current->m_count != 0) + { + return; + } + } // Ensure we are disposed. - Object *tmp = const_cast (this); - const Object *end = this; - do { - NS_ASSERT (current != 0); - Object *next = tmp->m_next; - if (!tmp->m_disposed) - { - tmp->DoDispose (); - } - tmp = next; - } while (tmp != end); + for (uint32_t i = 0; i < n; i++) + { + Object *current = m_aggregates->buffer[i]; + if (!current->m_disposed) + { + current->DoDispose (); + } + } // all attached objects have a zero count so, - // we can delete all attached objects. - current = this; - do { - NS_ASSERT (current != 0); - Object *next = current->m_next; - delete current; - current = next; - } while (current != end); + // we can delete them all. + struct Aggregates *aggregates = m_aggregates; + for (uint32_t i = 0; i < n; i++) + { + // There is a trick here: each time we call delete below, + // the deleted object is removed from the aggregate buffer + // in the destructor so, the index of the next element to + // lookup is always zero + Object *current = aggregates->buffer[0]; + delete current; + } } } // namespace ns3 diff --git a/src/core/object.h b/src/core/object.h index 721c7453b..03564d7e5 100644 --- a/src/core/object.h +++ b/src/core/object.h @@ -85,9 +85,9 @@ public: Ptr Next (void); private: friend class Object; - AggregateIterator (Ptr first); - Ptr m_first; - Ptr m_current; + AggregateIterator (Ptr object); + Ptr m_object; + uint32_t m_current; }; Object (); @@ -215,6 +215,21 @@ private: friend class ObjectFactory; friend class AggregateIterator; + /** + * This data structure uses a classic C-style trick to + * hold an array of variable size without performing + * two memory allocations: the declaration of the structure + * declares a one-element array but when we allocate + * memory for this struct, we effectively allocate a larger + * chunk of memory than the struct to allow space for a larger + * variable sized buffer whose size is indicated by the element + * 'n' + */ + struct Aggregates { + uint32_t n; + Object *buffer[1]; + }; + Ptr DoGetObject (TypeId tid) const; bool Check (void) const; bool CheckLoose (void) const; @@ -243,6 +258,8 @@ private: */ void Construct (const AttributeList &attributes); + void UpdateSortedArray (struct Aggregates *aggregates, uint32_t i) const; + /** * The reference count for this object. Each aggregate * has an individual reference count. When the global @@ -261,13 +278,19 @@ private: */ bool m_disposed; /** - * A pointer to the next aggregate object. This is a circular - * linked list of aggregated objects: the last one points - * back to the first one. If an object is not aggregated to - * any other object, the value of this field is equal to the - * value of the 'this' pointer. + * a pointer to an array of 'aggregates'. i.e., a pointer to + * each object aggregated to this object is stored in this + * array. The array is shared by all aggregated objects + * so the size of the array is indirectly a reference count. */ - Object *m_next; + struct Aggregates * m_aggregates; + /** + * Indicates the number of times the object was accessed with a + * call to GetObject. This integer is used to implement a + * heuristic to sort the array of aggregates to put at the start + * of the array the most-frequently accessed elements. + */ + uint32_t m_getObjectCount; }; /**