JavaScript'te Dizi ve Nesne verimliliği


145

Binlerce nesne içeren bir modelim var. Onları depolamanın ve tek bir nesneyi aldıktan sonra en etkili yolun ne olacağını merak ediyordum. Kimlikler uzun sayılardır.

Bunlar benim düşündüğüm 2 seçenek. Birinci seçenekte, artan bir endekse sahip basit bir dizi. 2. seçenekte, ilişkisel bir dizi ve belki bir fark yaratıyorsa, bir nesne olabilir. Benim sorum, hangisinin daha verimli olduğu, çoğunlukla tek bir nesneyi almam gerektiğinde, ancak bazen bunlar arasında dolaşıp sıralayabileceğim.

İlişkilendirilmemiş diziye sahip birinci seçenek:

var a = [{id: 29938, name: 'name1'},
         {id: 32994, name: 'name1'}];
function getObject(id) {
    for (var i=0; i < a.length; i++) {
        if (a[i].id == id) 
            return a[i];
    }
}

İlişkilendirilebilir diziyle ikinci seçenek:

var a = [];  // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
    return a[id];
}

Güncelleme:

Tamam, ikinci seçenekte bir dizi kullanmak söz konusu değil olsun. Dolayısıyla, ikinci seçenek olan bildirim satırı gerçekten şöyle olmalıdır: var a = {};ve tek soru şudur: belirli bir kimliğe sahip bir nesneyi almanın daha iyi performans gösterdiği şey: bir dizi veya kimliğin anahtar olduğu bir nesne.

ve ayrıca, listeyi birçok kez sıralamak zorunda kalırsam cevap değişecek mi?



Her zaman sıralanmış bir koleksiyona mı ihtiyacınız var? Öyleyse, bir diziden başka bir seçenek yoktur (şu anda yaptığınız gibi dizinleri kullanmasa da).
Jon

@Jon aslında, biliyorum. "şu anda yaptığınız gibi" ne demek istiyorsun?
Moshe Shaham

1
@MosheShaham: Dizilerin 0'dan başlayarak sürekli dizinleri olmalıdır (dizileri olmalıdır) Dizileri kullanıyorsanız, başka bir şey yapmayın.
Jon

Sanırım bu kıyaslama sorunuzun ilk kısmına cevap verecektir: jsben.ch/#/Y9jDP
EscapeNetscape

Yanıtlar:


143

Kısa sürüm: Diziler çoğunlukla nesnelerden daha hızlıdır. Ancak% 100 doğru çözüm yoktur.

Güncelleme 2017 - Test ve Sonuçlar

var a1 = [{id: 29938, name: 'name1'}, {id: 32994, name: 'name1'}];

var a2 = [];
a2[29938] = {id: 29938, name: 'name1'};
a2[32994] = {id: 32994, name: 'name1'};

var o = {};
o['29938'] = {id: 29938, name: 'name1'};
o['32994'] = {id: 32994, name: 'name1'};

for (var f = 0; f < 2000; f++) {
    var newNo = Math.floor(Math.random()*60000+10000);
    if (!o[newNo.toString()]) o[newNo.toString()] = {id: newNo, name: 'test'};
    if (!a2[newNo]) a2[newNo] = {id: newNo, name: 'test' };
    a1.push({id: newNo, name: 'test'});
}

Test kurulumu Test sonuçları

Orijinal Mesaj - Açıklama

Sorunuzda bazı yanılgılar var.

Javascript'te ilişkilendirilebilir diziler yok. Sadece Diziler ve Nesneler.

Bunlar diziler:

var a1 = [1, 2, 3];
var a2 = ["a", "b", "c"];
var a3 = [];
a3[0] = "a";
a3[1] = "b";
a3[2] = "c";

Bu da bir dizi:

var a3 = [];
a3[29938] = "a";
a3[32994] = "b";

Temelde içinde delikler bulunan bir dizi, çünkü her dizi sürekli indekslemeye sahiptir. Deliksiz dizilerden daha yavaştır. Ancak dizi boyunca manuel olarak yineleme yapmak daha yavaştır (çoğunlukla).

Bu bir nesne:

var a3 = {};
a3[29938] = "a";
a3[32994] = "b";

İşte üç olasılıktan oluşan bir performans testi:

Lookup Array vs Holey Array vs Object Performance Test

Smashing Magazine'de bu konular hakkında mükemmel bir okuma: Hızlı bellek verimli JavaScript yazma


1
@Moshe Ve böylece Javascript'teki performans hakkındaki tüm tartışmalar yapılmalıdır. : P
deceze

9
Bu gerçekten üzerinde çalıştığınız verilerin ve verilerin boyutuna bağlıdır. Çok küçük veri setleri ve küçük nesneler dizilerde çok daha iyi performans gösterir. Arama hakkında konuşmanız, bir nesneyi harita olarak kullandığınız büyük bir veri kümesindeyse, nesne daha verimlidir. jsperf.com/array-vs-object-performance/35
f1v

5
F1v ile aynı fikirde olmakla birlikte, Revizyon 35'in testte bir kusuru vardır: if (a1[i].id = id) result = a1[i];Olması gereken: if (a1[i].id === id) result = a1[i];Test http://jsperf.com/array-vs-object-performance/37 bunu düzeltir
Charles Byrne

1
Bkz. Http://jsperf.com/array-vs-object-performance/71 . 5000 ile karşılaştırıldığında yaklaşık 93 nesnenin daha küçük bir veri alt kümesi (veri oluşturma için ilmekledi, ama dizi delik istedim) vardır. Dış döngü Ids nesne dizisi (başlangıç ​​orta ve son) dağınık aramak için ve Array aramasının tüm öğeleri hareket ettirmesi için eksik bir kimlik de ekledim. Holey Array, Key by Object, ardından Manual Array. Dolayısıyla f1v'nin belirttiği gibi, gerçekten verilerin boyutuna ve verilerin manuel dizi aramaları için nerede olduğuna bağlıdır.
Charles Byrne

4
Bu cevap, bu yazıdaki jsPerf sonuçları özetlenerek geliştirilebilir - özellikle jsPerf sonuçları sorunun gerçek cevabı olduğu için. Gerisi ekstra. Bu, jsPerf'in kapalı olduğu zamanlarda daha önemlidir (şu anda olduğu gibi). meta.stackexchange.com/questions/8231/…
Jeff

23

Diziler ve nesneler çok farklı çalıştığı (veya en azından olması gerektiği) için bu gerçekten bir performans sorusu değil. Diziler sürekli bir dizine 0..nsahipken, nesneler rastgele anahtarları rastgele değerlerle eşler. Eğer siz belirli tuşlara tedarik istiyorum, tek seçenek bir nesnedir. Anahtarları umursamıyorsanız, bir dizi.

Bir dizide rasgele (sayısal) anahtarlar ayarlamaya çalışırsanız, gerçekten performans kaybınız olur , çünkü davranışsal olarak dizi aradaki tüm dizinleri doldurur:

> foo = [];
  []
> foo[100] = 'a';
  "a"
> foo
  [undefined, undefined, undefined, ..., "a"]

(Dizinin aslında 99 undefineddeğer içermediğini unutmayın , ancak diziyi bir noktada yinelediğiniz için bu şekilde davranacaktır .)

Her iki seçeneğin değişmez değerleri, nasıl kullanılabileceklerini çok netleştirmelidir:

var arr = ['foo', 'bar', 'baz'];     // no keys, not even the option for it
var obj = { foo : 'bar', baz : 42 }; // associative by its very nature

Belirli anahtarlar vermek istemiyorum. Neyin daha iyi performans gösterdiğini bilmek istiyorum ve bununla çalışacağım. Tamam, ikinci seçenekte bir dizi söz konusu değil. Peki ya ilişkili olmayan bir diziye karşı bir nesne?
Moshe Shaham

1
@Moshe Javascript'te ilişkisel olmayan bir dizi diye bir şey yoktur. Tuşlara (sayılara veya dizelere) ihtiyacınız varsa bir nesne kullanın. Sadece (sıralı) bir listeye ihtiyacınız varsa dizileri kullanın. Dönemi. Performans tartışmaya girmez. Performans çok önemliyse ve anahtarlarınızla her iki şekilde de yaşayabiliyorsanız hangisinin sizin için daha iyi çalıştığını deneyin.
deceze

5
Ama neyin daha iyi performans gösterdiğini bilmek istiyorum: bir diziden (içinden döngü yaparak) veya id anahtar olduğu bir "ilişkilendirilebilir" nesneden bir nesne almak. Sorum açık değilse üzgünüm ...
Moshe Shaham

2
@Moshe Bir nesneye veya diziye anahtarla herhangi bir şeye erişirseniz, her zaman istediğiniz şeyi bulmaya çalışan kaptan döngüden çok daha hızlı olacaktır. Bir dizideki veya bir nesnedeki bir öğeye anahtarla erişme farkı muhtemelen önemsizdir. Döngü her iki durumda da daha kötüdür.
deceze

1
@deceze - Nasıl "dizi holding kullanıcı nesneleri hakkında ve kullanıcının nesnesini elde etmek için, user_id" nesne "anahtarları olan nesneye dayalı kullanıcı nesnesi elde etmek için bir döngü gerekir , user_idböylece kullanıcı nesnesi user_idanahtar olarak erişilebilir ". Hangisi performans açısından daha iyidir? Bu konuda herhangi bir öneriniz takdir :)
Rayon

13

ES6 ile en etkili yol Harita kullanmak olacaktır.

var myMap = new Map();

myMap.set(1, 'myVal');
myMap.set(2, { catName: 'Meow', age: 3 });

myMap.get(1);
myMap.get(2);

ES6 özelliklerini bugün bir dolgu kullanarak kullanabilirsiniz ( https://github.com/es-shims/es6-shim ).

Performans, tarayıcıya ve senaryoya göre değişir. Ancak Mapen çok performans gösteren örnek : https://jsperf.com/es6-map-vs-object-properties/2


REFERANS https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map


11
Bunu destekleyecek kaynağın var mı? Gözlemlerimden bugüne kadar ES6 Kümeleri dizilerden daha hızlı, ancak ES6 Haritaları hem nesnelerden hem de dizilerden daha yavaş
Steel Brain

1
Bu daha "semantik", daha fazla performans değil, soru buydu.
AlexG

3
@AlexG, başlığın açıkça belirtildiğinden oldukça emin efficiency.
Qix - MONICA

@Qix Evet, kötüyüm: o
AlexG

8

In NodeJS bilirseniz ID, dizinin döngü çok yavaş karşılaştırılır object[ID].

const uniqueString = require('unique-string');
const obj = {};
const arr = [];
var seeking;

//create data
for(var i=0;i<1000000;i++){
  var getUnique = `${uniqueString()}`;
  if(i===888555) seeking = getUnique;
  arr.push(getUnique);
  obj[getUnique] = true;
}

//retrieve item from array
console.time('arrTimer');
for(var x=0;x<arr.length;x++){
  if(arr[x]===seeking){
    console.log('Array result:');
    console.timeEnd('arrTimer');
    break;
  }
}

//retrieve item from object
console.time('objTimer');
var hasKey = !!obj[seeking];
console.log('Object result:');
console.timeEnd('objTimer');

Ve sonuçlar:

Array result:
arrTimer: 12.857ms
Object result:
objTimer: 0.051ms

Arayan kimliği dizide / nesnede ilk kişi olsa bile:

Array result:
arrTimer: 2.975ms
Object result:
objTimer: 0.068ms

5

Bunu kelimenin tam anlamıyla bir sonraki boyuta taşımaya çalıştım.

X ve y eksenlerinin her zaman aynı uzunlukta olduğu 2 boyutlu bir dizi verildiğinde, aşağıdakilerden daha hızlıdır:

a) iki boyutlu bir dizi oluşturarak ilk dizine ve ardından ikinci dizine bakarak hücreye bakın, yani:

var arr=[][]    
var cell=[x][y]    

veya

b) x ve y koordinatlarının dize ile temsil edilen bir nesne oluşturun ve ardından bu obj üzerinde tek bir arama yapın, yani:

var obj={}    
var cell = obj['x,y']    

Sonuç:
Dizilerde iki sayısal dizin araması yapmanın, nesnede bir özellik aramasından çok daha hızlı olduğu ortaya çıkıyor.

Sonuçlar burada:

http://jsperf.com/arr-vs-obj-lookup-2


3

Kullanıma bağlıdır. Durum arama nesneleri ise çok daha hızlıdır.

Dizi ve nesne aramalarının performansını test etmek için bir Plunker örneği.

https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p=preview

Bunu göreceksin; 5.000 uzunluk dizi koleksiyonunda 5.000 öğe ararken miliscon'ları devralın3000

Ancak nesnede 5.000 öğe ararken 5.000 özellik vardır, yalnızca al 2veya 3miliscon

Ayrıca nesne ağacı yapmak büyük bir fark yaratmaz


0

Benzer bir sorun yaşadım, x öğelerle sınırlı bir olay kaynağından canlı şamdanlar depolamam gerekiyor. Her mumun zaman damgasının anahtar olarak hareket edeceği ve mumun kendisinin değer olarak hareket edeceği bir nesnede saklayabilirdim. Başka bir olasılık, her öğenin mumun kendisinin olduğu bir dizide saklayabilmemdi. Canlı mumlarla ilgili bir sorun, en son güncellemenin en son verileri içerdiği aynı zaman damgasında güncelleme göndermeye devam etmeleri, bu nedenle mevcut bir öğeyi güncellemeniz veya yeni bir tane eklemenizdir. İşte 3 olasılıkın tümünü birleştirmeye çalışan güzel bir kriter. Aşağıdaki çözeltideki diziler ortalama olarak en az 4 kat daha hızlıdır. Oynaması çekinmeyin

"use strict";

const EventEmitter = require("events");
let candleEmitter = new EventEmitter();

//Change this to set how fast the setInterval should run
const frequency = 1;

setInterval(() => {
    // Take the current timestamp and round it down to the nearest second
    let time = Math.floor(Date.now() / 1000) * 1000;
    let open = Math.random();
    let high = Math.random();
    let low = Math.random();
    let close = Math.random();
    let baseVolume = Math.random();
    let quoteVolume = Math.random();

    //Clear the console everytime before printing fresh values
    console.clear()

    candleEmitter.emit("candle", {
        symbol: "ABC:DEF",
        time: time,
        open: open,
        high: high,
        low: low,
        close: close,
        baseVolume: baseVolume,
        quoteVolume: quoteVolume
    });



}, frequency)

// Test 1 would involve storing the candle in an object
candleEmitter.on('candle', storeAsObject)

// Test 2 would involve storing the candle in an array
candleEmitter.on('candle', storeAsArray)

//Container for the object version of candles
let objectOhlc = {}

//Container for the array version of candles
let arrayOhlc = {}

//Store a max 30 candles and delete older ones
let limit = 30

function storeAsObject(candle) {

    //measure the start time in nanoseconds
    const hrtime1 = process.hrtime()
    const start = hrtime1[0] * 1e9 + hrtime1[1]

    const { symbol, time } = candle;

    // Create the object structure to store the current symbol
    if (typeof objectOhlc[symbol] === 'undefined') objectOhlc[symbol] = {}

    // The timestamp of the latest candle is used as key with the pair to store this symbol
    objectOhlc[symbol][time] = candle;

    // Remove entries if we exceed the limit
    const keys = Object.keys(objectOhlc[symbol]);
    if (keys.length > limit) {
        for (let i = 0; i < (keys.length - limit); i++) {
            delete objectOhlc[symbol][keys[i]];
        }
    }

    //measure the end time in nano seocnds
    const hrtime2 = process.hrtime()
    const end = hrtime2[0] * 1e9 + hrtime2[1]

    console.log("Storing as objects", end - start, Object.keys(objectOhlc[symbol]).length)
}

function storeAsArray(candle) {

    //measure the start time in nanoseconds
    const hrtime1 = process.hrtime()
    const start = hrtime1[0] * 1e9 + hrtime1[1]

    const { symbol, time } = candle;
    if (typeof arrayOhlc[symbol] === 'undefined') arrayOhlc[symbol] = []

    //Get the bunch of candles currently stored
    const candles = arrayOhlc[symbol];

    //Get the last candle if available
    const lastCandle = candles[candles.length - 1] || {};

    // Add a new entry for the newly arrived candle if it has a different timestamp from the latest one we storeds
    if (time !== lastCandle.time) {
        candles.push(candle);
    }

    //If our newly arrived candle has the same timestamp as the last stored candle, update the last stored candle
    else {
        candles[candles.length - 1] = candle
    }

    if (candles.length > limit) {
        candles.splice(0, candles.length - limit);
    }

    //measure the end time in nano seocnds
    const hrtime2 = process.hrtime()
    const end = hrtime2[0] * 1e9 + hrtime2[1]


    console.log("Storing as array", end - start, arrayOhlc[symbol].length)
}

Sonuç 10 buradaki sınırdır

Storing as objects 4183 nanoseconds 10
Storing as array 373 nanoseconds 10

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.