大多數狀況下,代碼執行的效率能夠採用時間複雜度分析,可是大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; }