大多数状况下,代码执行的效率能够采用时间复杂度分析,可是大O表示法一般会省略常数。
可是在工业级实现中,真正的执行效率一般会考虑多方面:javascript
以上是理论层级,对于前端开发来讲,若是你开发的是基础库会被普遍使用时,只是基于理论的分析是不够的。
此时就能够借助一些基准库来对你的模块进行测试: benchmark.js前端
let myArray = [10000]; myArray[0] = 0; myArray[9999] = 1;
底层为链表,为了增长查找效率,在链表基础上,以(2/3/4/../n)个节点增长一层查找链表java
场景: 如Redis中的有序集合git
//https://github.com/v8/v8/blob/master/src/objects/js-objects.h // 咱们先来看JS中object的定义,继承自JSReceiver // Line 278 class JSObject : public JSReceiver { //省略... } // Line 26 // 接下来再看,JSReceiver继承自HeapObject,而且有几个重要属性 // JSReceiver includes types on which properties can be defined, i.e., // JSObject and JSProxy. class JSReceiver : public HeapObject { public: NEVER_READ_ONLY_SPACE // Returns true if there is no slow (ie, dictionary) backing store. // 是否有快速属性模式 inline bool HasFastProperties() const; // Returns the properties array backing store if it exists. // Otherwise, returns an empty_property_array when there's a Smi (hash code) or an empty_fixed_array for a fast properties map. // 属性数组 inline PropertyArray property_array() const; // Gets slow properties for non-global objects. // 字典属性 inline NameDictionary property_dictionary() const; // Sets the properties backing store and makes sure any existing hash is moved // to the new properties store. To clear out the properties store, pass in the // empty_fixed_array(), the hash will be maintained in this case as well. void SetProperties(HeapObject properties); // There are five possible values for the properties offset. // 1) EmptyFixedArray/EmptyPropertyDictionary - This is the standard // placeholder. // // 2) Smi - This is the hash code of the object. // // 3) PropertyArray - This is similar to a FixedArray but stores // the hash code of the object in its length field. This is a fast // backing store. // // 4) NameDictionary - This is the dictionary-mode backing store. // // 4) GlobalDictionary - This is the backing store for the // GlobalObject. // 初始化属性 inline void initialize_properties();
// https://github.com/v8/v8/blob/master/src/objects/dictionary.h // 咱们先来看看继承链 // Line 202 class V8_EXPORT_PRIVATE NameDictionary : public BaseNameDictionary<NameDictionary, NameDictionaryShape>{} // Line 128 class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary : public Dictionary<Derived, Shape> {} // Line 26 class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Dictionary : public HashTable<Derived, Shape> {} // 由上可知继承自HashTable,咱们来看HashTable的定义 // https://github.com/v8/v8/blob/master/src/objects/hash-table.h // 而且在文件开头的注释已经很详细了 // HashTable is a subclass of FixedArray that implements a hash table that uses open addressing and quadratic probing. *重要: hash表使用数组为基础数据,并在其上实现了开放寻址和二次探测 // In order for the quadratic probing to work, elements that have not yet been used and elements that have been deleted are distinguished. // Probing continues when deleted elements are encountered and stops when unused elements are encountered. * 为了使二次探测工做正常,未使用/被删除的元素将被标记删除而不是直接删除 // - Elements with key == undefined have not been used yet. // - Elements with key == the_hole have been deleted. // 如下会被使用的hash表派生类 // Line 292 template <typename Derived, typename Shape> class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) ObjectHashTableBase : public HashTable<Derived, Shape> {} 接下来咱们看看V8在实现hash表时的几个重要行为和参数 // https://github.com/v8/v8/blob/master/src/objects/objects.cc 1. 扩容 // Line 7590 // 下面源代码是新增一个元素后的逻辑 ObjectHashTableBase<Derived, Shape>::Put(Isolate* isolate, Handle<Derived> table, Handle<Object> key, Handle<Object> value, int32_t hash) { int entry = table->FindEntry(roots, key, hash); // Key is already in table, just overwrite value. // key已经存在,覆盖值 if (entry != kNotFound) { table->set(Derived::EntryToValueIndex(entry), *value); return table; } // 若是33%以上元素都被删除了,考虑从新hash // Rehash if more than 33% of the entries are deleted entries. // TODO(jochen): Consider to shrink the fixed array in place. if ((table->NumberOfDeletedElements() << 1) > table->NumberOfElements()) { table->Rehash(roots); } // If we're out of luck, we didn't get a GC recently, and so rehashing isn't enough to avoid a crash. // 若是没有足够的预估空位,按二倍大小进行从新hash if (!table->HasSufficientCapacityToAdd(1)) { int nof = table->NumberOfElements() + 1; int capacity = ObjectHashTable::ComputeCapacity(nof * 2); if (capacity > ObjectHashTable::kMaxCapacity) { for (size_t i = 0; i < 2; ++i) { isolate->heap()->CollectAllGarbage( Heap::kNoGCFlags, GarbageCollectionReason::kFullHashtable); } table->Rehash(roots); } } // Line 6583. // 下面是计算预估空位的逻辑 HashTable<Derived, Shape>::EnsureCapacity(Isolate* isolate, Handle<Derived> table, int n, AllocationType allocation) { if (table->HasSufficientCapacityToAdd(n)) return table; int capacity = table->Capacity(); int new_nof = table->NumberOfElements() + n; const int kMinCapacityForPretenure = 256; bool should_pretenure = allocation == AllocationType::kOld || ((capacity > kMinCapacityForPretenure) && !Heap::InYoungGeneration(*table)); Handle<Derived> new_table = HashTable::New( isolate, new_nof, should_pretenure ? AllocationType::kOld : AllocationType::kYoung); table->Rehash(ReadOnlyRoots(isolate), *new_table); return new_table; } 2. 收缩 // Line 6622 HashTable<Derived, Shape>::Shrink(Isolate* isolate, Handle<Derived> table, int additionalCapacity) { int capacity = table->Capacity(); int nof = table->NumberOfElements(); // Shrink to fit the number of elements if only a quarter of the capacity is filled with elements. // 当只有1/4的装载量时,进行收缩 if (nof > (capacity >> 2)) return table; // Allocate a new dictionary with room for at least the current number of // elements + {additionalCapacity}. The allocation method will make sure that // there is extra room in the dictionary for additions. Don't go lower than // room for {kMinShrinkCapacity} elements. int at_least_room_for = nof + additionalCapacity; int new_capacity = ComputeCapacity(at_least_room_for); if (new_capacity < Derived::kMinShrinkCapacity) return table; if (new_capacity == capacity) return table; const int kMinCapacityForPretenure = 256; bool pretenure = (at_least_room_for > kMinCapacityForPretenure) && !Heap::InYoungGeneration(*table); Handle<Derived> new_table = HashTable::New(isolate, new_capacity, pretenure ? AllocationType::kOld : AllocationType::kYoung, USE_CUSTOM_MINIMUM_CAPACITY); table->Rehash(ReadOnlyRoots(isolate), *new_table); return new_table; }