Skip to content

Commit

Permalink
Improve Object creation performance
Browse files Browse the repository at this point in the history
* Convert rootedObjectStructureSet(Vector) into HashSet
* Use inline storage for properties in CreateObjectData

Signed-off-by: Seonghyun Kim <[email protected]>
  • Loading branch information
ksh8281 committed Jan 30, 2024
1 parent 322ea2a commit 78c55d5
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 43 deletions.
8 changes: 4 additions & 4 deletions src/interpreter/ByteCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -966,15 +966,15 @@ class CreateObjectPrepare : public ByteCode {
bool m_allPrecomputed;
bool m_wasStructureComputed;
bool m_canStoreStructureOnCode;
Vector<ObjectStructureItem, GCUtil::gc_malloc_allocator<ObjectStructureItem>> m_properties;
VectorWithInlineStorage<6, ObjectStructureItem, GCUtil::gc_malloc_allocator<ObjectStructureItem>> m_properties;
EncodedValueVector m_values;
Object* m_target;
CreateObjectPrepare* m_initCode;
CreateObjectData(bool allPrecomputed, size_t reserveSize, Object* target, bool wasStructureComputed,
CreateObjectPrepare* initCode)
CreateObjectData(bool allPrecomputed, bool wasStructureComputed, bool canStoreStructureOnCode,
size_t reserveSize, Object* target, CreateObjectPrepare* initCode)
: m_allPrecomputed(allPrecomputed)
, m_wasStructureComputed(wasStructureComputed)
, m_canStoreStructureOnCode(allPrecomputed)
, m_canStoreStructureOnCode(canStoreStructureOnCode)
, m_target(target)
, m_initCode(initCode)
{
Expand Down
19 changes: 8 additions & 11 deletions src/interpreter/ByteCodeInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2985,23 +2985,20 @@ NEVER_INLINE void InterpreterSlowPath::createObjectOperation(ExecutionState& sta
if (!data->m_wasStructureComputed) {
size_t propertyCount = data->m_properties.size();
Optional<ObjectStructure*> cache;
if (data->m_allPrecomputed && ObjectStructure::isTransitionModeAvailable(propertyCount)) {
if (data->m_allPrecomputed) {
cache = state.context()->vmInstance()->findRootedObjectStructure(data->m_properties.data(), propertyCount);
}
if (cache) {
obj->m_structure = cache.value();
} else {
obj->m_structure = ObjectStructure::create(state.context(),
ObjectStructureItemTightVector(data->m_properties.data(), data->m_properties.data() + propertyCount),
#if defined(ESCARGOT_SMALL_CONFIG)
false);
#else
data->m_allPrecomputed);
#endif
data->m_allPrecomputed);
if (data->m_canStoreStructureOnCode) {
data->m_initCode->m_cachedObjectStructure = obj->m_structure;
data->m_initCode->m_cachedObjectStructure->markReferencedByInlineCache();
byteCodeBlock->m_otherLiteralData.pushBack(obj->m_structure);
auto structure = obj->m_structure;
data->m_initCode->m_cachedObjectStructure = structure;
byteCodeBlock->m_otherLiteralData.pushBack(structure);
state.context()->vmInstance()->addObjectStructureToRootSet(structure);
}
}
}
Expand Down Expand Up @@ -3047,8 +3044,8 @@ NEVER_INLINE void InterpreterSlowPath::createObjectPrepareOperation(ExecutionSta
ptr = &registerFile[code->m_dataRegisterIndex];
}
CreateObjectPrepare::CreateObjectData* data = new (ptr) CreateObjectPrepare::CreateObjectData(
code->m_allPrecomputed, code->m_propertyReserveSize,
new Object(state), code->m_cachedObjectStructure.hasValue(), code);
code->m_allPrecomputed, code->m_cachedObjectStructure.hasValue(), code->m_allPrecomputed,
code->m_propertyReserveSize, new Object(state), code);
registerFile[code->m_objectIndex] = data->m_target;
if (data->m_wasStructureComputed) {
data->m_target->m_structure = code->m_cachedObjectStructure.value();
Expand Down
4 changes: 1 addition & 3 deletions src/runtime/ObjectStructure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ ObjectStructure* ObjectStructure::create(Context* ctx, ObjectStructureItemTightV
if (!isTransitionModeAvailable(properties.size())) {
return new ObjectStructureWithMap(hasIndexStringAsPropertyName, hasSymbol, hasEnumerableProperty, std::move(properties));
} else if (preferTransition) {
ObjectStructure* newObjectStructure = new ObjectStructureWithTransition(std::move(properties), hasIndexStringAsPropertyName, hasSymbol, hasNonAtomicPropertyName, hasEnumerableProperty);
ctx->vmInstance()->rootedObjectStructure().pushBack(newObjectStructure);
return newObjectStructure;
return new ObjectStructureWithTransition(std::move(properties), hasIndexStringAsPropertyName, hasSymbol, hasNonAtomicPropertyName, hasEnumerableProperty);
} else {
return new ObjectStructureWithoutTransition(new ObjectStructureItemVector(std::move(properties)), hasIndexStringAsPropertyName, hasSymbol, hasNonAtomicPropertyName, hasEnumerableProperty);
}
Expand Down
38 changes: 38 additions & 0 deletions src/runtime/ObjectStructure.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,23 @@ class ObjectStructure : public gc {
return m_isReferencedByInlineCache;
}

bool hasSamePropertiesTo(const ObjectStructure* src) const
{
size_t myCount = propertyCount();
size_t srcCount = src->propertyCount();
if (myCount == srcCount) {
auto myProperties = properties();
auto srcProperties = src->properties();
for (size_t j = 0; j < myCount; j++) {
if (myProperties[j].m_propertyName != srcProperties[j].m_propertyName || myProperties[j].m_descriptor != srcProperties[j].m_descriptor) {
return false;
}
}
return true;
}
return false;
}

protected:
ObjectStructure(bool hasIndexPropertyName,
bool hasSymbolPropertyName, bool hasEnumerableProperty)
Expand Down Expand Up @@ -221,6 +238,27 @@ class ObjectStructure : public gc {
uint8_t m_transitionTableVectorBufferCapacity : 8;
};

struct ObjectStructureEqualTo {
bool operator()(const ObjectStructure* x, const ObjectStructure* y) const
{
return x->hasSamePropertiesTo(y);
}
};

struct ObjectStructureHash {
size_t operator()(const ObjectStructure* x) const
{
size_t propertyCount = x->propertyCount();
size_t result = propertyCount;
size_t hashPropertyCount = std::min(propertyCount, static_cast<size_t>(6));
auto properties = x->properties();
for (size_t i = 0; i < hashPropertyCount; i++) {
result += properties[i].m_propertyName.hashValue();
}
return result;
}
};

class ObjectStructureWithoutTransition : public ObjectStructure {
public:
ObjectStructureWithoutTransition(ObjectStructureItemVector* properties, bool hasIndexPropertyName,
Expand Down
104 changes: 85 additions & 19 deletions src/runtime/VMInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,26 @@ void VMInstance::compressStringsIfNeeds(uint64_t currentTickCount)
}
#endif

static void markHashSet(GC_word* desc, size_t base)
{
#if defined(COMPILER_MSVC) || defined(COMPILER_CLANG_CL)
GC_set_bit(desc, base + 2); // m_ht.m_buckets_data
GC_set_bit(desc, base + 5); // m_ht.m_buckets
#else
GC_set_bit(desc, base + 1); // m_ht.m_buckets_data
GC_set_bit(desc, base + 4); // m_ht.m_buckets
#endif
}

void* VMInstance::operator new(size_t size)
{
static MAY_THREAD_LOCAL bool typeInited = false;
static MAY_THREAD_LOCAL GC_descr descr;
if (!typeInited) {
GC_word desc[GC_BITMAP_SIZE(VMInstance)] = { 0 };
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_staticStrings.dtoaCache));
// we should mark every word of m_atomicStringMap
for (size_t i = 0; i < sizeof(m_atomicStringMap); i += sizeof(size_t)) {
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_atomicStringMap) + (i / sizeof(size_t)));
}

markHashSet(desc, GC_WORD_OFFSET(VMInstance, m_atomicStringMap));
#define DECLARE_GLOBAL_SYMBOLS(name) GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_globalSymbols.name));
DEFINE_GLOBAL_SYMBOLS(DECLARE_GLOBAL_SYMBOLS);
#undef DECLARE_GLOBAL_SYMBOLS
Expand All @@ -165,7 +173,7 @@ void* VMInstance::operator new(size_t size)
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_defaultStructureForMappedArgumentsObject));
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_defaultStructureForUnmappedArgumentsObject));
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_defaultPrivateMemberStructure));
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_rootedObjectStructure));
markHashSet(desc, GC_WORD_OFFSET(VMInstance, m_rootedObjectStructure));
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_onVMInstanceDestroyData));
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_toStringRecursionPreventer.m_registeredItems));
GC_set_bit(desc, GC_WORD_OFFSET(VMInstance, m_regexpCache));
Expand Down Expand Up @@ -615,22 +623,80 @@ DateObject* VMInstance::cachedUTC(ExecutionState& state)
return m_cachedUTC;
}

void VMInstance::addObjectStructureToRootSet(ObjectStructure* structure)
{
ASSERT(m_rootedObjectStructure.find(structure) == m_rootedObjectStructure.end());
m_rootedObjectStructure.insert(structure);
// if non-tranition structure not marked by inlinecache,
// properties can be disappear if modifying property function is called
structure->markReferencedByInlineCache();
}

class TempObjectStructure : public ObjectStructure {
public:
TempObjectStructure(ObjectStructureItem* properties, size_t propertyCount)
: ObjectStructure(false, false, false)
, m_properties(properties)
, m_propertyCount(propertyCount)
{
}

virtual std::pair<size_t, Optional<const ObjectStructureItem*>> findProperty(const ObjectStructurePropertyName& s) override
{
return std::make_pair(0, nullptr);
}

virtual const ObjectStructureItem& readProperty(size_t idx) override
{
return m_properties[idx];
}

virtual const ObjectStructureItem* properties() const override
{
return m_properties;
}

virtual size_t propertyCount() const override
{
return m_propertyCount;
}

virtual ObjectStructure* addProperty(const ObjectStructurePropertyName& name, const ObjectStructurePropertyDescriptor& desc) override
{
return nullptr;
}

virtual ObjectStructure* removeProperty(size_t pIndex) override
{
return nullptr;
}

virtual ObjectStructure* replacePropertyDescriptor(size_t idx, const ObjectStructurePropertyDescriptor& newDesc) override
{
return nullptr;
}

virtual ObjectStructure* convertToNonTransitionStructure() override
{
return nullptr;
}

virtual bool inTransitionMode() override
{
return false;
}

private:
ObjectStructureItem* m_properties;
size_t m_propertyCount;
};

Optional<ObjectStructure*> VMInstance::findRootedObjectStructure(ObjectStructureItem* properties, size_t propertyCount)
{
for (size_t i = 0; i < m_rootedObjectStructure.size(); i++) {
if (m_rootedObjectStructure[i]->propertyCount() == propertyCount) {
auto cachedProperties = m_rootedObjectStructure[i]->properties();
bool match = true;
for (size_t j = 0; j < propertyCount; j++) {
if (cachedProperties[j].m_propertyName != properties[j].m_propertyName || cachedProperties[j].m_descriptor != properties[j].m_descriptor) {
match = false;
break;
}
}
if (match) {
return m_rootedObjectStructure[i];
}
}
TempObjectStructure temp(properties, propertyCount);
auto iter = m_rootedObjectStructure.find(&temp);
if (iter != m_rootedObjectStructure.end()) {
return *iter;
}
return nullptr;
}
Expand Down
7 changes: 2 additions & 5 deletions src/runtime/VMInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,7 @@ class VMInstance : public gc {
return m_didSomePrototypeObjectDefineIndexedProperty;
}

Vector<ObjectStructure*, GCUtil::gc_malloc_allocator<ObjectStructure*>>& rootedObjectStructure()
{
return m_rootedObjectStructure;
}
void addObjectStructureToRootSet(ObjectStructure* structure);
Optional<ObjectStructure*> findRootedObjectStructure(ObjectStructureItem* properties, size_t propertyCount);

void somePrototypeObjectDefineIndexedProperty(ExecutionState& state);
Expand Down Expand Up @@ -427,7 +424,7 @@ class VMInstance : public gc {

ObjectPrivateMemberStructure* m_defaultPrivateMemberStructure;

Vector<ObjectStructure*, GCUtil::gc_malloc_allocator<ObjectStructure*>> m_rootedObjectStructure;
HashSet<ObjectStructure*, ObjectStructureHash, ObjectStructureEqualTo, GCUtil::gc_malloc_allocator<ObjectStructure*>> m_rootedObjectStructure;

std::vector<ByteCodeBlock*> m_compiledByteCodeBlocks;
size_t m_compiledByteCodeSize;
Expand Down
2 changes: 1 addition & 1 deletion src/util/Vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ class VectorWithInlineStorage : public gc {
{
ASSERT(siz >= size());
if (LIKELY(!m_useExternalStorage)) {
if (siz >= InlineStorageSize) {
if (siz > InlineStorageSize) {
m_useExternalStorage = true;
m_externalStorage.reserve(siz);

Expand Down

0 comments on commit 78c55d5

Please sign in to comment.