Skip to content

Simple and flat ArrayTable #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion graph/Graph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ Graph::makePrevPaths(Vertex *vertex,
}

PathVertexRep *
Graph::prevPaths(Vertex *vertex) const
Graph::prevPaths(Vertex *vertex)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loss of one indirection means we can't use const here, but if we're getting non-const pointers to vertices, then I would argue the graph is not actually const.

{
return prev_paths_.pointer(vertex->prevPaths());
}
Expand Down
92 changes: 41 additions & 51 deletions include/sta/ArrayTable.hh
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public:
uint32_t count);
// Grow as necessary and return pointer for id.
TYPE *ensureId(ObjectId id);
TYPE *pointer(ObjectId id) const;
TYPE &ref(ObjectId id) const;
TYPE *pointer(ObjectId id);
TYPE &ref(ObjectId id);
size_t size() const { return size_; }
void clear();

Expand All @@ -58,19 +58,16 @@ public:

private:
ArrayBlock<TYPE> *makeBlock(uint32_t size);
void pushBlock(ArrayBlock<TYPE> *block);
void pushBlock(size_t size);
void deleteBlocks();

size_t size_;
// Block index of free block (blocks_[size - 1]).
BlockIdx free_block_idx_;
// Index of next free object in free_block_idx_.
ObjectIdx free_idx_;
// Don't use std::vector so growing blocks_ can be thread safe.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you missed the comment that you deleted that explains why std::vector cannot be used here. If you look at the commit history you will see that it used to use std::vector. When the the vector grows it fails with multiple threads.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't miss it. vector::swap is basically no different from the previous approach. See https://godbolt.org/z/8sqqTPMMf

However, I suppose implementations other than libstdc++ might exhibit different behavior on lower optimization levels. So I pushed a manual swap implementation just to be sure.

size_t blocks_size_;
size_t blocks_capacity_;
ArrayBlock<TYPE>* *blocks_;
ArrayBlock<TYPE>* *prev_blocks_;
std::vector<ArrayBlock<TYPE>> blocks_;
std::vector<ArrayBlock<TYPE>> prev_blocks_;
// Linked list of free arrays indexed by array size.
std::vector<ObjectId> free_list_;
static constexpr ObjectId idx_mask_ = block_size - 1;
Expand All @@ -80,28 +77,24 @@ template <class TYPE>
ArrayTable<TYPE>::ArrayTable() :
size_(0),
free_block_idx_(block_idx_null),
free_idx_(object_idx_null),
blocks_size_(0),
blocks_capacity_(1024),
blocks_(new ArrayBlock<TYPE>*[blocks_capacity_]),
prev_blocks_(nullptr)
free_idx_(object_idx_null)
{
blocks_.reserve(1024);
}

template <class TYPE>
ArrayTable<TYPE>::~ArrayTable()
{
deleteBlocks();
delete [] blocks_;
delete [] prev_blocks_;
}

template <class TYPE>
void
ArrayTable<TYPE>::deleteBlocks()
{
for (size_t i = 0; i < blocks_size_; i++)
delete blocks_[i];
for (size_t i = 0; i < blocks_.size(); i++)
blocks_[i].free();
blocks_.clear();
}

template <class TYPE>
Expand All @@ -120,12 +113,12 @@ ArrayTable<TYPE>::make(uint32_t count,
free_list_[count] = *head;
}
else {
ArrayBlock<TYPE> *block = blocks_size_ ? blocks_[free_block_idx_] : nullptr;
ArrayBlock<TYPE> *block = blocks_.size() ? &blocks_[free_block_idx_] : nullptr;
if ((free_idx_ == object_idx_null
&& free_block_idx_ == block_idx_null)
|| free_idx_ + count >= block->size()) {
uint32_t size = block_size;
if (blocks_size_ == 0
if (blocks_.size() == 0
// First block starts at idx 1.
&& count > block_size - 1)
size = count + 1;
Expand All @@ -145,32 +138,36 @@ template <class TYPE>
ArrayBlock<TYPE> *
ArrayTable<TYPE>::makeBlock(uint32_t size)
{
BlockIdx block_idx = blocks_size_;
ArrayBlock<TYPE> *block = new ArrayBlock<TYPE>(size);
pushBlock(block);
BlockIdx block_idx = blocks_.size();
pushBlock(size);
free_block_idx_ = block_idx;
// ObjectId zero is reserved for object_id_null.
free_idx_ = (block_idx > 0) ? 0 : 1;
return block;
return &blocks_[block_idx];
}

template <class TYPE>
void
ArrayTable<TYPE>::pushBlock(ArrayBlock<TYPE> *block)
ArrayTable<TYPE>::pushBlock(size_t size)
{
blocks_[blocks_size_++] = block;
if (blocks_size_ >= block_id_max)
blocks_.push_back(ArrayBlock<TYPE>(size));

if (blocks_.size() >= block_id_max)
criticalError(223, "max array table block count exceeded.");
if (blocks_size_ == blocks_capacity_) {
size_t new_capacity = blocks_capacity_ * 1.5;
ArrayBlock<TYPE>** new_blocks = new ArrayBlock<TYPE>*[new_capacity];
memcpy(new_blocks, blocks_, blocks_capacity_ * sizeof(ArrayBlock<TYPE>*));
if (prev_blocks_)
delete [] prev_blocks_;
if (blocks_.size() == blocks_.capacity()) {
prev_blocks_.reserve(blocks_.capacity() * 1.5);
const auto blocks_mid = blocks_.begin() + prev_blocks_.size();
std::copy(blocks_.begin(), blocks_mid, prev_blocks_.begin());
prev_blocks_.insert(prev_blocks_.end(), blocks_mid, blocks_.end());
// Preserve block array for other threads to reference.
prev_blocks_ = blocks_;
blocks_ = new_blocks;
blocks_capacity_ = new_capacity;
// Swap the vectors while keeping blocks_ valid.
// vector::swap is usually basically the same,
// but a naive implementation might temporarily set blocks_ to null.
const size_t vec_size = sizeof(blocks_);
std::uint8_t tmp[vec_size];
std::memcpy(&tmp, static_cast<void*>(&blocks_), vec_size);
std::memcpy(static_cast<void*>(&blocks_), static_cast<void*>(&prev_blocks_), vec_size);
std::memcpy(static_cast<void*>(&prev_blocks_), &tmp, vec_size);
}
}

Expand All @@ -191,14 +188,14 @@ ArrayTable<TYPE>::destroy(ObjectId id,

template <class TYPE>
TYPE *
ArrayTable<TYPE>::pointer(ObjectId id) const
ArrayTable<TYPE>::pointer(ObjectId id)
{
if (id == object_id_null)
return nullptr;
else {
BlockIdx blk_idx = id >> idx_bits;
ObjectIdx obj_idx = id & idx_mask_;
return blocks_[blk_idx]->pointer(obj_idx);
return blocks_[blk_idx].pointer(obj_idx);
}
}

Expand All @@ -209,31 +206,29 @@ ArrayTable<TYPE>::ensureId(ObjectId id)
BlockIdx blk_idx = id >> idx_bits;
ObjectIdx obj_idx = id & idx_mask_;
// Make enough blocks for blk_idx to be valid.
for (BlockIdx i = blocks_size_; i <= blk_idx; i++) {
ArrayBlock<TYPE> *block = new ArrayBlock<TYPE>(block_size);
pushBlock(block);
for (BlockIdx i = blocks_.size(); i <= blk_idx; i++) {
pushBlock(block_size);
}
return blocks_[blk_idx]->pointer(obj_idx);
return blocks_[blk_idx].pointer(obj_idx);
}

template <class TYPE>
TYPE &
ArrayTable<TYPE>::ref(ObjectId id) const
ArrayTable<TYPE>::ref(ObjectId id)
{
if (id == object_id_null)
criticalError(222, "null ObjectId reference is undefined.");

BlockIdx blk_idx = id >> idx_bits;
ObjectIdx obj_idx = id & idx_mask_;
return blocks_[blk_idx]->ref(obj_idx);
return blocks_[blk_idx].ref(obj_idx);
}

template <class TYPE>
void
ArrayTable<TYPE>::clear()
{
deleteBlocks();
blocks_size_ = 0;
size_ = 0;
free_block_idx_ = block_idx_null;
free_idx_ = object_idx_null;
Expand All @@ -246,11 +241,12 @@ template <class TYPE>
class ArrayBlock
{
public:
ArrayBlock() = default;
ArrayBlock(uint32_t size);
~ArrayBlock();
uint32_t size() const { return size_; }
TYPE &ref(ObjectIdx idx) { return objects_[idx]; }
TYPE *pointer(ObjectIdx idx) { return &objects_[idx]; }
void free() { delete[] objects_; }

private:
uint32_t size_;
Expand All @@ -264,10 +260,4 @@ ArrayBlock<TYPE>::ArrayBlock(uint32_t size) :
{
}

template <class TYPE>
ArrayBlock<TYPE>::~ArrayBlock()
{
delete [] objects_;
}

} // Namespace
2 changes: 1 addition & 1 deletion include/sta/Graph.hh
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public:
size_t requiredCount() const { return requireds_.size(); }
PathVertexRep *makePrevPaths(Vertex *vertex,
uint32_t count);
PathVertexRep *prevPaths(Vertex *vertex) const;
PathVertexRep *prevPaths(Vertex *vertex);
void clearPrevPaths();
// Reported slew are the same as those in the liberty tables.
// reported_slews = measured_slews / slew_derate_from_library
Expand Down