LINUX.ORG.RU

Луа5.1 и параноидальная оптимизация

 


1

3
local sub, gsub, random = string.sub, string.gsub, math.random
local SendAddonMessage = SendAddonMessage

-- Таблица смещений
local STR_OFFSETS = {}
for i = 1, 100 do
    STR_OFFSETS[i] = (i-1)*3 + 1
end

function time100_Server(channel, text, sender, prefix)
    if prefix ~= "time100" then return end

    local objFull = mFldS:getStaticStr(sender, 1) or ""
    local hpFull = mFldS:getStaticStr(sender, 2) or ""

    local rezT, rezF = {}, {}
    local obj_char, hp_val, formattedHp

    for i = 1, 100 do
        local offset = STR_OFFSETS[i]
        if offset + 2 > #objFull then break end

        obj_char = sub(objFull, offset, offset + 2)
        hp_val = en10(sub(hpFull, offset, offset + 2) or "000")

        if obj_char == "00f" then
            if hp_val < 999 then
                formattedHp = sub("   "..en85(hp_val + 1), -3)
                rezF[#rezF+1] = formattedHp
                mFldS:addStaticStr(sender, 2, i, formattedHp)
            else
                mFldS:addStaticStr(sender, 1, i, "00t")
                mFldS:addStaticStr(sender, 2, i, en85(999))
                rezT[#rezT+1] = "999"
            end
        elseif obj_char == "00t" then
            if hp_val < 999 then
                hp_val = hp_val + 1
            elseif hp_val >= 999 and random(10) == 1 then
                hp_val = hp_val + 1
            end
            formattedHp = sub("   "..en85(hp_val), -3)
            rezT[#rezT+1] = formattedHp
            mFldS:addStaticStr(sender, 2, i, formattedHp)
        end
    end

    if text == "1" then
        local function sendChunked(base, data)
            if #data == 0 then return end
            data = gsub(data, " ", "%0")
            --SendAddonMessage(base.." "..sender, sub(data, 1, 150), "GUILD")
            if #data > 150 then
                --SendAddonMessage(base.."_2 "..sender, sub(data, 151), "GUILD")
            end
        end
        sendChunked("time100_00t", table.concat(rezT))
        sendChunked("time100_00f", table.concat(rezF))
    end
end

Допустим есть такой вот код на луа. Тут мы получаем строку с «объектами». Строка всегда 300 символов, каждый объект всегда 3 символа. Если находим два нужных объекта, итерируем им «здоровье». Записываем изменения в базу и при необходимости отправляем об этом общее уведомление в эфир.

Чтение и изменение строки происходит самописными методами:

-- Добавление или обновление строки
function NsDb:addStaticStr(nik, nStr, nArg, message)
    self.input_table[nik] = self.input_table[nik] or {}

    if not nArg then
        self.input_table[nik][nStr] = message
        return
    end

    -- Обновляем строку
    local currentStr = self.input_table[nik][nStr]
    self.input_table[nik][nStr] = currentStr:sub(1, (nArg - 1) * 3)
                                .. ("   " .. message):sub(-3)
                                .. currentStr:sub(nArg * 3 + 1)
end

function NsDb:getStaticStr(nik, nStr, nArg)
    local str = self.input_table 
              and self.input_table[nik] 
              and self.input_table[nik][nStr]
    
    return str and (nArg and str_sub(str, (nArg-1)*3 + 1, nArg*3) or str)
end

И вот на 10000 проходов задержка где то на секунду и отжор памяти метров на 15.

Избавляться от локальных переменных пробовал - получается криво. Попробовал вон перейти вместо прямого саба и конкатенации на регулярки и матч, всеравно плохо.

А можно ли еще что то тут такое придумать, чтобы одновременно снизить скорость прохода и локальное потребление памяти?

Я понимаю, что сборщик памяти все очистит. Но он это делает довольно редко - раз в 3 минуты и мне как концепцию - можно ли сделать лучше?

Может можно еще что нибудь, что я сейас не вижу?

OBJECT_POSITION_PATTERNS = {}
for i = 1, 100 do  -- Важно: цикл до 100, а не 10!
    OBJECT_POSITION_PATTERNS[i] = "^" .. string.rep("...", i - 1) .. "(...)"
end

Например, я пробовал вот так обращаться к объектам. Но вроде это не лучше.

Короче, решено: самая оптимальная работа на луа со строками через саб. Быстрее и дешевле нет ничего. Прямое обращение саб к подстроке и все. Без локальных переменных и кэширований.

★★★★★

Последнее исправление: LightDiver (всего исправлений: 3)
Ответ на: комментарий от MOPKOBKA

Обвязка - проверки на существование элементов в таблице, например.

А с таким синтаксисом я познакомился именно в раст. Для меня это синтаксис раст. И тут бах - я его встречаю в питоне! Вчера бота для телеграма срочно пришлось написать:

https://github.com/Vladgobelen/NStgb

Делаю нужные методы, скармливаю дикпику, чтобы он прокомментировал, отформатировал все, а он такой и предлагает мне указать типы о_О. Я аж охренел. Думаю - языки перепутал чтоли. Запускаю - а оно запускается.

LightDiver ★★★★★
() автор топика
Последнее исправление: LightDiver (всего исправлений: 1)
Ответ на: комментарий от LightDiver

Обвязка - проверки на существование элементов в таблице, например.

Стандартная библиотека слишком маленькая, да.

Делаю нужные методы, скармливаю дикпику, чтобы он прокомментировал, отформатировал все, а он такой и предлагает мне указать типы о_О

Их недавно ввели, пару лет назад. Еще размеченный ими код очень хорошо автодополняется и обрабатывается IDE.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Между кортежом и списком я разницы не знаю

Mutability

Список это изменяемый (mutable) тип данных, кортеж - неизменяемый. Поэтому кортеж быстрее списка и жрет меньше памяти при работе с ним.

Obezyan
()
Ответ на: комментарий от LightDiver

Потому что lua не обязывает финализировать выражения, тут

local x math.random(1, 100)

Тоже самое что и

local x; math.random(1, 100);

или

local x = nil; math.random(1, 100);

А вот тут, тоже самое что и

local x = nil; 
(1 + 2)

Где скобку пропускаем как задающую порядок вычисления, выражение 1 + 2 вызывает ошибку так как херня какая то :D
Ошибка логического восприятия того что выглядит похоже, но имеет абсолютно разную суть =)

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 3)
Ответ на: комментарий от LightDiver

Луа-скрипт (chunk) - это последовательность утверждений (statement), опционально разделенных «;».

Перевод строки - это просто пробельный символ.

Утверждение (statement) - это:

  • присваивание, скобки «()» только в выражениях справа от «=»
  • вызов функции
  • do блок
  • if, while, for и др. управляющие конструкции
  • [local] объявление функции
  • local переменные [с присваиванием]
local x x=(42+0) print(
x,
(x/2)
)

anonymous
()
Ответ на: комментарий от LightDiver

Я слишком вольно выразился (исправил).

Просто есть правила синтаксиса

И код разбирающий эти правила ожидает определённые закономерности, вот и всё.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от Obezyan

Поэтому кортеж быстрее списка

Почему? Только за счет невыделения дополнительного capacity?

Неизменяемость мне кажется слабым аргументом в пользу отдельного типа. Лучше freeze функцию или модификатор типа const.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

можно глянут в сырцы :)

tuple а это замороженный list ( ибо элементы можно менять у тупла чисто хэндлы постоянны за содержимое через хэндл никто и никогда гарантий не давал)

просто быстрее ибо его можно id передавать всегда ибо константно ну и копировать там где это улучшит общий ффефект

qulinxao3 ★☆
()
Ответ на: комментарий от MOPKOBKA

Почему? Только за счет невыделения дополнительного capacity?

В кортеже все элементы константны, это позволяет кешировать хеш элементов. Также из-за постоянного размера память выделяется один раз при создании.

В списке не используется кеш и память выделяется динамически из-за возможности добавления/изменения/удаления элементов.

Потому кортеж быстрее списка.

Obezyan
()
Ответ на: комментарий от Obezyan

невсегда

ссылки константны но если элемент список например то этим тюплом уже не ключанёшь словарь (и не сунешь в set)

зы. Гвидо(экая фамильярность) сожалеет что из за ошибки прикрутил тьюплы как отдельный тип

qulinxao3 ★☆
()
Последнее исправление: qulinxao3 (всего исправлений: 1)
Ответ на: комментарий от qulinxao3

Вы правы, я намерено не указал частные случаи чтобы не усложнять объяснение. В этом случае хеш будет -1 если я правильно помню и команды типа hash() будут возвращать Type Error.

Obezyan
()
Ответ на: комментарий от Obezyan

В кортеже все элементы константны, это позволяет кешировать хеш элементов.

Какой хеш? Не разбираюсь в Python. Вызов hash([]) выдает ошибку, что говорит о том что рассуждение о хеше к спискам вроде как неприменимо вообще.

В списке не используется кеш и память выделяется динамически из-за возможности добавления/изменения/удаления элементов.

Наверное list сразу аллоцирует память для 16 элементов, или на второй шаг растит capacity до значения размера обычного tuple.

Я решил написать простой бенчмарк на создание list/tuple, и получил такие цифры:

Повторений 100000000
tuple 2.5669189000000188s
list 2.86208009999973s

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

что говорит о том что рассуждение о хеше к спискам вроде как неприменимо

Это говорит о том что list не имеет хеша, об этом я написал в предыдущем посте.

Я решил написать простой бенчмарк на создание list/tuple, и получил такие цифры:

И подтвердили мои слова о том что tuple быстрее list.

Obezyan
()
Ответ на: комментарий от Obezyan

Это говорит о том что list не имеет хеша

А tuple не имеет append, но о чем это говорит, только о том что в list не сделали hash для Python?

И подтвердили мои слова о том что tuple быстрее list.

Это какое то очень слабое различие, думал там 2x и более, я решил отследить до CPython что происходит между [i, j, k] vs (i, j, k) и увидел что они создаются практически идентично, для списка тоже выделяется 3 элемента сразу, и значения заполняются в С цикле.

Реальная разница есть в константных значениях, [1, 2, 3] vs (1, 2, 3), компилятор Python в таком случае возвращает всегда один Tuple, но это работает только в пределах одной функции.

>>> def test(): return (1, 2, 3)
>>> test() is test()
True

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)