Почему цикл с помощью массива намного быстрее, чем собственный `indexOf` JavaScript?

Почему цикл через массив намного быстрее, чем JavaScript native indexOf? Есть ли ошибка или что-то, что я не учитываю? Я ожидал, что исходные реализации будут быстрее.

                For Loop        While Loop      indexOf
Chrome 10.0     50,948,997      111,272,979     12,807,549
Firefox 3.6     9,308,421       62,184,430      2,089,243
Opera 11.10     11,756,258      49,118,462      2,335,347   

http://jsben.ch/#/xm2BV

Ответ 1

Через 5 лет в браузерах произошли большие изменения. Теперь производительность indexOf увеличилась и, безусловно, лучше, чем любая другая альтернативная альтернатива.

Версия 49.0.2623.87 (64-разрядная версия)

Версия Chrome 49.0.2623.87 (64-разрядная версия)

Ответ 2

Вероятно, потому что фактическая реализация indexOf делает намного больше, чем просто цикл через массив. Вы можете увидеть внутреннюю реализацию Firefox здесь:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf

Есть несколько вещей, которые могут замедлить цикл, который существует для разумных целей:

  • Массив перебрасывается в Object
  • fromIndex преобразуется в число
  • Они используют Math.max вместо тройного
  • Они используют Math.abs

Ответ 3

indexOf выполняет кучу проверки и проверки типов, которые игнорируются в цикле for и while.

Здесь алгоритм indexOf:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf

Изменить. Мое предположение: indexOf быстрее для больших массивов, потому что он кэширует длину массива перед его прохождением.

Ответ 4

Запустите тест еще раз с редактированием, которое я сделал.

Я увеличил размер массива и сделал индекс, который вы ищите, больше. Кажется, что в больших массивах indexOf может быть более быстрым выбором.

http://jsben.ch/#/xm2BV

РЕДАКТИРОВАТЬ: на основе большего количества тестов indexOf работает быстрее, чем цикл for в версии Safari, который я использую (5.0.3) и медленнее почти во всем остальном.

Ответ 5

Возможно, стоит заметить, что если все, что вы пытаетесь сделать, это сохранить список элементов и проверить наличие (например, избегать добавления повторяющихся идентификаторов в массив), было бы гораздо быстрее сохранить OBJECT с ключами, которые отражают каждый идентификатор. Если вы считаете, что я ошибаюсь, сравните следующее с массивом + indexOf. Мы говорим о 181ms для метода объекта против 1 МИНУТ для метода indexOf массива.

var objs = []
var i_uid = {} // method 1
var a_uid = [] // method 2
var total_count = 100000, idLen = 5
var ts, te, cObj = 0

// method 1
ts = new Date()
while (cObj < total_count) {
    var u = uid(idLen),
        o = {
            uid: u,
            text: 'something',
            created: new Date()
        }
    if (!i_uid[u]) { // ensure unique uids only
        objs.push(o)
        i_uid[u] = cObj // current array position as placeholder
        cObj++
    }
    else {
        console.log('unique violation [duplicate uid', u, ']')
    }
}
te = new Date()
console.log('loaded ' + total_count + ' with object method in', (te - ts), 'ms')

i_uid = {} // free-up
cObj = 0 // reset
objs = [] // reset

// method 2
ts = new Date()
while (cObj < total_count) {
    var u = uid(idLen),
        o = {
            uid: u,
            text: 'something',
            created: new Date()
        }
    if (a_uid.indexOf(u) == -1) { // ensure unique uids only
        objs.push(o)
        a_uid.push(u)
        cObj++
    }
    else {
        console.log('unique violation [duplicate uid', u, ']')
    }
}
te = new Date()
console.log('loaded ' + total_count + ' with array + indexOf method in', (te - ts), 'ms')

function uid(l) {
    var t = '',
        p = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
        pl = p.length
    for (var i = 0; i < l; i++)
        t += p.charAt(Math.floor(Math.random() * pl))
    return t
}

Ответ 6

Мы можем доверять простой в цикл за каждый раз.

Я написал код ниже, чтобы узнать ответ на этот вопрос. Это также runnable. Он показывает, что простой for loop является лучшим решением при рассмотрении производительности.

(Код также можно найти на jsfiddle)

console.clear()

let a = []
// populating array data
for (let i = 0; i < 100000; i++) {
	a.push(i)
}

let testNum = 90000
let found
let totalMS4ForLoop = 0
let totalMS4IndexOf = 0
let start
let end
// simulating 10000 requests which are come consecutively
for (o = 0; o < 10000; o++) {

  start = Date.now()
  for (let i = 0; i < a.length; i++) {
    if (a[i] == testNum) { found = a[i]; break }
  }
  end = Date.now()
  totalMS4ForLoop += end - start

  start = Date.now()
  found = a[a.indexOf(testNum)]
  end = Date.now()
  totalMS4IndexOf += end - start

}

console.log("10000 for-loop executions took total " + totalMS4ForLoop + " ms.")
console.log("10000 indexOf executions took total " + totalMS4IndexOf + " ms.")