Javascript.RU

Сравнение производительности Switch и If…else в JavaScript

Что будем делать ?
Попробуем сравнить разницу в производительности Switch и If-else.
Зачем?
Безумно интересно.
Как?
Напишем функции которая будет генерировать N переходов по списку M вариантов. Условно будем переходить или доходить до нужного элемента и делать break. Для всего это будем использовать Workers JS.
На чем?
Для тестов используем механизм Workers и браузеры поддерживающие этот механизм  FF, Chrome, Safari. IE в топку – он в любом случае будем тормозить.

Итак коды исходников:
run.js

// Вывод строки логов 
function logmsg(msg) {
    var lb = document.getElementById('output')
    lb.innerHTML += '> ' + msg + '<br>'
}

// объект описывающий методы нашего теста
var testObj = {
    // шаг тестирования
    testStep: 50,
    // число элементов
    elemCnt: 4000,
    // количество запусков 
    runCnt: 5000,
    // тип теста (switch/ifelse)
    test: 'switch',
    // Значение передаваемое тестируемой функцие
    runVal: 1,
    results: {
        // данные о тесте switch
        _switch: [],
        // данные о тесте if ... else
        ifelse: [],
        // массив точек тестирования
        points: []
    }
}

//объявляем Workers
var worker = new Worker('worker.js');
worker.onmessage = function(e) {
    var d = e.data
    calcPersent((testObj.elemCnt / testObj.testStep + 1) * 2)
    // записываем результаты теста
    testObj.results[d.test == 'switch' ? '_switch' : 'ifelse'].push(d.time)
    testObj.results.points.push(d.runVal)
    logmsg('task finish: ' + d.test + d.elemCnt + ' ( ' + d.runVal + ' ) x ' + d.runCnt + ' >> ' + d.time + 'ms')
    // запускаем следующий
    if (d.runVal == 1) d.runVal = 0
    if (d.runVal < d.elemCnt) runTest(d.runVal + testObj.testStep, d.test)
    else {
        if (d.test == 'switch') {
            d.test = 'ifelse'
            logmsg('<span>Run If...Else testing ...</span>')
            runTest(1, d.test)
        } else {
            logmsg('<span>Creating chart ...</span>')
            drawChart()
        }
    }
}

// функция постановки задачи тестирования в очередь
function runTest(runVal, test) {
    //logmsg('push task: (' + runVal + ', ' + test + ')')
    testObj.runVal = runVal
    testObj.test = test
    worker.postMessage(testObj)
}

// запускаем процессы
logmsg('<span>Run Switch testing ...</span>')
runTest(1, 'switch')

//рисуем график
function drawChart() {
    var data = new google.visualization.DataTable();
    data.addColumn('string', 'Call number');
    data.addColumn('number', 'Switch');
    data.addColumn('number', 'If...Else');
    var pointsVals = []
    with(testObj.results) {
        for (var i = 0; i < points.length / 2; i++) {
            pointsVals.push([points[i].toString(), _switch[i], ifelse[i]])
        }
    }
    data.addRows(pointsVals);
    var options = {
        width: 600,
        height: 300,
        title: 'Results chart'
    };
    logmsg('<div id="chartid"></div>')
    var chart = new google.visualization.LineChart(document.getElementById('chartid'));
    chart.draw(data, options);
}

var currP = false
docTitle = document.title

function calcPersent(fullP) {
    currP = currP || 1
    document.title = '(' + (currP / fullP * 100).toFixed(2) + '%)' + docTitle
    //console.log(title)
    currP++
}

worker.js

// создание функции с N элементами case
function createSwitchFun(N) {
    var funBody = 'switch (v){'
    for (var i = 0; i < N; i++) {
        funBody += 'case ' + (i + 1) + ': return v;'
    }
    funBody += '}'
    return Function('v', funBody)
}

// создание функции с N элементами if...else
function createIfElseFun(N) {
    var funBody = 'if (v==1){return v}'
    for (var i = 1; i < N; i++) {
        funBody += 'else if(v==' + (i + 1) + '){return v}'
    }
    return Function('v', funBody)
}

// функция запускаю функцию fun M раз со значением val и 
// возвращает время выполнения в милисекундах
function runFun(M, fun, val) {
    var start = new Date();
    for (var i = 0; i < M; i++) fun(val)
    var end = new Date();
    return end - start
}


// обрабатываем сообщения
self.onmessage = function(e) {  
	var fun = e.data.test == 'switch' ? 
		createSwitchFun(e.data.elemCnt) :
		createIfElseFun(e.data.elemCnt)
    e.data.time = runFun(e.data.runCnt, fun, e.data.runVal)
	postMessage(e.data);
};

В комментариях все достаточно подробно описано - останавливаться на кодах подробно не буду. А перейду сразу к результатам.

Результаты
Я запускали тесты с шагом в 50 т.е. подавал на поиск значения 1, 50, 100 …. и т.д., при количестве элементов 4000. По каждому значения я производил запуск 10000 раз. И в конце строил графики при помощи Гугла. И вот что получилось.

FF

Самые лучшие показатели на If и просто великолепные на Switch (очень похоже что он преобразует switch в объект)

Safari

Показывает отвратительные показатели для switch и средние для If

Chrome

Все показатели растут примерно одинаково, из этого мы можем сделать вывод, что switch работает как и положено на основе if else

Opera (для оперы пришлось делать фиксы в виде генерации функций на PHP и количестве вариантов 1000 и проводился тест с шагов в 10 и количеством в 100000, так что считать его сопоставим будем условно)

Switch видно что работает также как и в FF, а вот c if время выполнения неприятно растет

Выводы
Операторы которые во многих языках являются одним и тем же  - в различных браузерах реализованы по разному и зачастую не являются одним и тем же.

Live можно посмотреть здесь (работает только для FF, Chrome, Safari) http://sandbox.conceptblocks.ru/switch_ifelse_testing/

+3

Автор: poorking, дата: 14 января, 2012 - 17:52
#permalink

На IE9 тоже надо было сделать тесты, ведь [Цитирую]Безумно интересно.[\Цитирую]. Да и IE9 вполне нормальный браузер


Автор: jite, дата: 15 января, 2012 - 07:34
#permalink

Поддерживаю


Автор: Lenfer, дата: 16 января, 2012 - 23:56
#permalink

Мне вот тоже интересно конечно! НО в IE нету Worker, а использовать эмулятор этого дела совсем не хочется так как это сильно исказит показания!!!!


Автор: Denisko-Redisko, дата: 25 февраля, 2012 - 12:26
#permalink

Lenfer
Все показатели растут примерно одинаково, из этого мы можем сделать вывод, что switch работает как и положено на основе if else

А почему вы считаете, что switch положено реализовывать как набор if?
Ведь даже компиляторы могут (и часто так и делают) генерить таблицу переходов для switch.


Автор: oneguy, дата: 17 июня, 2012 - 18:13
#permalink

Замечание по поводу реализации функции logmsg: добавлять запись с помощью innerHTML+= плохо, так как если логов собралось много, то эта инструкция всё считывает и переписывает заново. Намного лучше для этого использовать appendChild.


 
Поиск по сайту
Другие записи этого автора
Больше записей нет. Прокомментируйте эту запись - может быть, тогда он что-нибудь еще хорошее напишет ;)
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Популярные таги
Последние темы на форуме
Forum