Вызов функции быстрее, чем расчет на лету?

Я сейчас серьезно запутался. У меня есть функция, создающая таблицу со случайным количеством записей, и я попробовал два разных метода, чтобы выбрать это число (которое несколько взвешено):

Метод 1, отдельная функция

local function n()
    local n = math.random()
    if n < .7 then return 0
    elseif n < .8 then return 1
    end
    return 2
end

local function final()
    for i = 1, n() do
        ...
    end
end

Способ 2, прямой расчет

local function final()
    local n = math.random()
    if n < .7 then n = 0
    elseif n < .8 then n = 1
    else n = 2
    end
    for i = 1, n do
        ...
    end
end

Проблема в следующем: по какой-то причине первый метод работает на 30% быстрее, чем второй. Почему это?


person user6245072    schedule 19.05.2016    source источник
comment
Действительно? Я бы сказал, что метод 1 будет медленнее из-за повторного глобального доступа к глобальному math.   -  person lhf    schedule 20.05.2016
comment
Как вы это измерили?   -  person Oleg V. Volkov    schedule 20.05.2016
comment
Я бы проголосовал за второй метод, так как нет накладных расходов на вызов функции.   -  person macroland    schedule 20.05.2016
comment
@Ihf количество обращений к math одинаковое, один.   -  person user6245072    schedule 20.05.2016
comment
@Олег В. Волков local t = os.clock() for i = 1, 1000 do final() end print((os.clock() - t) / 1000)   -  person user6245072    schedule 20.05.2016
comment
В любом случае функция final является частью более крупного метода, который выполняет некоторые операции и циклы на основе числа, определенного здесь, вчера я пытался вызвать более крупный метод и измерить его. Я могу поклясться, что первый метод занимал около 0,05 секунды каждый раз, когда я его запускал, а второй — 0,07. Попытка позвонить только final сегодня показала, что вы, ребята, правы, второй быстрее.   -  person user6245072    schedule 20.05.2016
comment
Он делает это снова! Дерьмо. Думаю все это переписать, так как еще и медленнее, чем моя предыдущая версия этой штуки.   -  person user6245072    schedule 20.05.2016
comment
если вы так отчаянно нуждаетесь в производительности, вам, возможно, следует подумать о другом языке. :)   -  person Piglet    schedule 20.05.2016
comment
Вы уверены, что ваша ОС не занята чем-то другим, пока вы запускаете этот код?   -  person macroland    schedule 20.05.2016
comment
@ user6245072, это очень грубый тест. os.clock не так точен, а 1000 итераций слишком мало для такой маленькой функции. В основном вы измеряли случайные сбои в работе вашего процессора.   -  person Oleg V. Volkov    schedule 20.05.2016
comment
@Piglet, Lua и так довольно быстр, а в воплощении JIT он почти так же быстр, как собственный код.   -  person Oleg V. Volkov    schedule 20.05.2016
comment
Я не особо разбираюсь в производительности, я просто переписываю некоторые модули для игры, которую пишу, и делать их медленнее просто бессмысленно.   -  person user6245072    schedule 20.05.2016
comment
Это происходит даже со старым модулем. Первый способ быстрее. Может ли это быть что-то, связанное с предсказанием ветвления? Однажды я читал что-то о функциях, хранящих результат для заданного ввода, поэтому в следующий раз, когда функция будет вызываться с тем же вводом, она уже знает результат, но я думаю, что это было о C++, также эта функция не принимает ввод, и я думаю, что случайность исключает предсказание ветвления, так что... я должен опубликовать весь код?   -  person user6245072    schedule 20.05.2016
comment
если lua достаточно быстр для nginx и redis, он достаточно быстр для большинства целей. @Piglet   -  person Dan    schedule 20.05.2016
comment
Весь код здесь   -  person user6245072    schedule 20.05.2016


Ответы (1)


Нет, вызов никогда не будет быстрее, чем его простое встраивание. Вся разница для первого метода заключается в добавлении дополнительной работы по настройке стека и его разборке. Остальной код, как исходный, так и скомпилированный, абсолютно одинаков, поэтому вполне естественно, что «просто вычисление» будет быстрее, чем «просто вычисление + дополнительная работа».

Ваш ориентир кажется неточным. Для такой легковесной функции сам цикл for и вызов os.clock займут почти столько же времени, сколько и сама функция, поэтому в сочетании с присущим os.clock низким разрешением и небольшим количеством циклов ваши данные на самом деле не являются статистически значимыми, и вы в основном видите результаты случайные сбои в вашем оборудовании. Используйте лучший таймер и увеличьте количество циклов как минимум до 1000000.

person Oleg V. Volkov    schedule 20.05.2016
comment
Лол уже долго ждать 50 - 70 секунд на 1000 тестов. - person user6245072; 20.05.2016
comment
@ user6245072, в вашем коде нет ничего, что могло бы вызвать такую ​​задержку. За исключением части ..., которую вы не опубликовали. Измерьте без него, чтобы увидеть реальную разницу. - person Oleg V. Volkov; 20.05.2016
comment
Дело в том, что без него метод с одной функцией работает быстрее, чем ожидалось. С ним метод двух функций работает быстрее. Я разместил код здесь - person user6245072; 20.05.2016