Перейти к содержимому
  • 0
rootmaster

сборка мусора слишком долгая и программа вылетает с too long without yielding

Вопрос

  1. что создает мусор в lua?
  2. как исправить столь долгую сборку мусора?
  3. какие операции с памятью не создают мусор?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Рекомендуемые сообщения

13 часа назад, rootmaster сказал:

сборка мусора слишком долгая и программа вылетает с too long without yielding

Любопытный случай. А как сборка мусора может привести к ошибке too long without yielding? Я не представляю, как это возможно. Приведи пример кода.

 

13 часа назад, rootmaster сказал:

что создает мусор в lua?

Мусор в Lua создаёт система автоматического управления памятью при уничтожении объектов.

 

13 часа назад, rootmaster сказал:

какие операции с памятью не создают мусор?

Мусор не создают операции, которые не приводят к уничтожению объектов.

 

13 часа назад, rootmaster сказал:

как исправить столь долгую сборку мусора?

Сколь именно долгую?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
35 минут назад, eu_tomat сказал:

Любопытный случай. А как сборка мусора может привести к ошибке too long without yielding? Я не представляю, как это возможно. Приведи пример кода.

к сожаления пример кода привести не могу так как я там уже все пофиксил, увеличив частоту сборки мусора)))

36 минут назад, eu_tomat сказал:

Мусор в Lua создаёт система автоматического управления памятью при уничтожении объектов.

ну это я знаю

36 минут назад, eu_tomat сказал:

Мусор не создают операции, которые не приводят к уничтожению объектов.

логично, но что делать если мне в цикле нужно сплитить строку, и использовать подстроки?

37 минут назад, eu_tomat сказал:

Сколь именно долгую?

настолько долгую что на сервере 5 секунд 0 tps O_O(я да, я уже собираюсь использовать это в своих целях)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
7 минут назад, rootmaster сказал:

к сожаления пример кода привести не могу так как я там уже все пофиксил, увеличив частоту сборки мусора)))

os.sleep(0)

или что ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Только что, serafim сказал:

os.sleep(0)

или что ?

os.sleep(0.2), раньше вызывался по таймеру на computer.uptime теперь вызывается каждые 512 инструкций интерпретатора моего языка программирования brown, который я сейчас пишу

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
23 минуты назад, rootmaster сказал:

s.sleep(0.2), раньше вызывался по таймеру на computer.uptime теперь вызывается каждые 512 инструкций интерпретатора моего языка программирования brown, который я сейчас пишу

можешь попробовать вызывать os.sleep(0) каждую итерацию, теоритически задержка не будет ощутима, но защитит от tlwy

Изменено пользователем serafim

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Только что, serafim сказал:

можешь попробовать вызывать os.sleep(0) каждую итерацию, поидеи задержка не будет ощутима, но защитит от tlwy

задержка будет очень ощутима, говорю по личному опыту, вызов каждые 512 итераций норм решения

 

кстати "поидеи" пишется как "по идее", даже мне безграмотному это режет глаз, сорян

Изменено пользователем rootmaster

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Да задержка всё же есть :(

Пример на 1000 итераций

local computer = require("computer")

local time_start = computer.uptime()
local count = 0
for i = 1, 1000 do
  count = count + 1
  os.sleep(0)
end
print("готово "..os.date("%M:%S",computer.uptime()-time_start))

без слипа

QZ8wyqR.png

со слипом

LYhNd6X.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1 минуту назад, serafim сказал:

Да задержка всё же есть :(

Пример на 1000 итераций


local computer = require("computer")

local time_start = computer.uptime()
local count = 0
for i = 1, 1000 do
  count = count + 1
  os.sleep(0)
end
print("готово "..os.date("%M:%S",computer.uptime()-time_start))

без слипа

QZ8wyqR.png

со слипом

LYhNd6X.png

да, а теперь попробуй вызывать os.sleep(0) только каждые 512 интераций(подсказка "i % 512 == 0")

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
10 минут назад, rootmaster сказал:

попробуй вызывать os.sleep(0) только каждые 512 интераций

Скрытый текст

local computer = require("computer")

local time_start = computer.uptime()
local count = 0
for i = 1, 1000 do
  count = count + 1
  if i % 512 == 0 then
    os.sleep(0)
    print("os.sleep(0)")
  end
end
print("готово "..os.date("%M:%S",computer.uptime()-time_start))

 

норм QZ8wyqR.png

Изменено пользователем serafim

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Только что, serafim сказал:

норм QZ8wyqR.png

ну вот тебе и ответ, почему я сделал такое решения:)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
15 часов назад, rootmaster сказал:

сборка мусора слишком долгая и программа вылетает с too long without yielding

1 час назад, rootmaster сказал:

я там уже все пофиксил, увеличив частоту сборки мусора)))

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Сразу подскажу: сборка мусора происходит на каждый десятый вызов computer.pullSignal.

Я вот тоже делал вещь, требующую очень много памяти: рендер пиксельных картинок на очках, вроде бы из OpenGlasses. Создание графического объекта возвращало прокси, а мне целиком этот прокси не был нужен. Так там память кончалась очень быстро, и приходилось десять раз запускать os.sleep(0).

Жаль, что нельзя сразу сказать "мне этот объект не нужен, Lua, пожалуйста, удали его прямо сейчас".

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
3 часа назад, ProgramCrafter сказал:

Сразу подскажу: сборка мусора происходит на каждый десятый вызов computer.pullSignal.

Я вот тоже делал вещь, требующую очень много памяти: рендер пиксельных картинок на очках, вроде бы из OpenGlasses. Создание графического объекта возвращало прокси, а мне целиком этот прокси не был нужен. Так там память кончалась очень быстро, и приходилось десять раз запускать os.sleep(0).

Жаль, что нельзя сразу сказать "мне этот объект не нужен, Lua, пожалуйста, удали его прямо сейчас".

по каким то причинам в Lua OpenComputers удален метод collectgarbage() который как раз принудительно пинает сборщик мусора

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
14 минуты назад, Laine_prikol сказал:

по каким то причинам в Lua OpenComputers удален метод collectgarbage() который как раз принудительно пинает сборщик мусора

Причина, скорее всего, та же, по которой была добавлена ошибка TLWY: излишне агрессивные программы, создающие высокую нагрузку на сервер, должны почаще уступать ресурсы. Все же знают игроков в Майнкрафт: ради экономии планки памяти мы готовы постоянно дёргать сборщик мусора, чего обычно не происходит в более реальных применениях. А существующая механика OpenComputers позволяет запрашивать уборку мусора не чаще чем один раз в 125 ms. Это и так много — 8 раз в секунду можно прибраться. По факту получается несколько реже, т.к. часть этого времени использует сама программа.

 

4 часа назад, ProgramCrafter сказал:

приходилось десять раз запускать os.sleep(0)

К слову, не всегда требуется запускать именно 10 раз. Бывает так, что к текущему моменту os.sleep, например, уже запускался раз 5, а внутри него было 8 вызовов computer.pullSignal. Знание этих фактов позволяет сократить ожидание перед уборкой мусора, если хочется выжать максимум.

 

Также желательно вызывать серию computer.pullSignal не пачкой по 10 штук, а более размеренно чередовать их вызовы с вычислительной нагрузкой. Это позволит программам на других компьютерах поддерживать ритмичность их работы, что очень важно, например, для управления ядерными реакторами.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Могу ли я как то узнать или высчитать, что вот сейчас machine упадёт по TLWY? Ну realTime() мне не узнать, она локальная. Я так же знаю что через 5 реальных секунд, если я не освободил поток, то machine упадёт по TLWY. Есть ли способы из под виртуального пространства, что даёт мод, из её песочницы, заранее узнать/узнавать что, вот-вот и если я не сделаю паузу в вычислениях, то machine упадёт по TLWY.

Изменено пользователем num_pi

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@num_pi, технически realTime хоста можно узнать через создание временного файла в /tmp/ и fs.lastModified(), хотя тут будет вполне достаточно computer.uptime или os.clock

 

@rootmaster, столкнулся с TLWY, когда требовалось конвертировать жирные 5-мегабайтные картинки из PNG в свой формат. Проблему решил через вызов pullSignal раз в несколько итераций обработки пикселей и несколько секунд работы компьютера. Эти числа подбираются эмпирическим путём, т.к. все зависит от тяжести алгоритма и TPS сервера, хотя, думаю, более умные люди смогут вычислить идеальные значения. Но сам концепт прост как три копейки и работает безотказно:

-- Снимаем нагрузку на индексацию таблиц в основном цикле
local computerUptime, computerPullSignal = computer.uptime, computer.pullSignal

local
    timeLimit,
    iterationsLimit,
    iteration,
    uptime = 2, 200, 0, computerUptime()

local deadline = uptime + timeLimit

while true do
    -- Деляем грязные делишки
  
    iteration = iteration + 1
    
    -- Чекаем, нужно ли вообще что-то предпринимать для текущей итерации
    if iteration > iterationsLimit then
      iteration, newUptime = 0, computer.uptime()
      
      -- Чекаем, нужно ли yieldиться с учётом затраченного времени
      uptime = computerUptime()
      
      if uptime > deadline then
          computerPullSignal(0)
          deadline = uptime + timeLimit
      end
    end
end

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
4 часа назад, num_pi сказал:

Могу ли я как то узнать или высчитать, что вот сейчас machine упадёт по TLWY? Ну realTime() мне не узнать, она локальная. Я так же знаю что через 5 реальных секунд, если я не освободил поток, то machine упадёт по TLWY. Есть ли способы из под виртуального пространства, что даёт мод, из её песочницы, заранее узнать/узнавать что, вот-вот и если я не сделаю паузу в вычислениях, то machine упадёт по TLWY.

Единственно верным ориентиром для оценки приближения TLWY является значение os.clock().

Более-менее устойчиво работающий код может выглядеть, например, так:

computer.pullSignal(0)
c0 = os.clock()+4
while os.clock()<c0 do
  -- вычисления
  -- ...
end

На устойчивость влияют:

  • Доступный резерв времени. В данном случае 1 секунда из 5 доступных секунд (по умолчанию).
  • Тяжесть кода, производящего вычисления.
  • Быстродействие сервера, то есть, его способность обработать вычислительную часть кода.
  • Текущая загрузка сервера другими процессами, никак не связанными с выполняемым кодом.

Приведённый код способен устойчиво работать в течение длительного времени, но полной гарантии, что его выполнение не закончится ошибкой, конечно же, нет.

 

Точное прогнозирование TLWY возможно на слабонагруженных серверах. Но на реальных серверах внезапно появившаяся нагрузка может сломать любой прогноз.

 

Выше я упоминал, что пачку из 10 вызовов computer.pullSignal(0) желательно распределять во времени. Такой подход кроме прочих его преимуществ позволяет снизить вероятность получить TLWY. Полагаю, этот момент не требует объяснений.

 

 

Изменено пользователем eu_tomat
опечатка

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

То есть как понял я,  у меня есть ровно 5 секунд, для занятия потока, для того что бы выполнить вычисления, и если я не успею это сделать ровно в 5 секунд реального времени то мод убьёт мой "процесс", и комп упадёт по TWYL?  

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
2 часа назад, num_pi сказал:

То есть как понял я,  у меня есть ровно 5 секунд, для занятия потока, для того что бы выполнить вычисления, и если я не успею это сделать ровно в 5 секунд реального времени то мод убьёт мой "процесс", и комп упадёт по TWYL?  

Да, примерно так. Возможно, не 5 секунд, если изменить значение в файле конфигурации. И реальность времени там очень условна. Сколько времени займёт выполнение того или иного кода, спрогнозировать сложно. Но именно os.clock даёт точное понимание того, сколько этого времени осталось.

 

Процесс не убивается, но генерируется исключение. Его можно перехватить, запустив код через pcall. Вместе с ошибкой TLWY ты получишь ещё 0.5 секунд времени на то, чтобы уступить время, например, вызвав computer.pullSignal, но успеешь ли ты это сделать, тоже никто не обещает. И новый слой оборачивания вызова кода в pcall уже точно не поможет.

 

Рассматривай доступное тебе время как потребительский кредит. Возвращать его или платить по нему проценты не требуется, т.к. уступка времени закрывает предшествующий ей кредит. Главное, не превышать отпущенный лимит, значение которого всегда заранее известно. Проблема в том, что ты получаешь этот кредит всегда реальным товаром, стоимость которого узнаёшь лишь после совершения сделки. Пока не купишь, стоимость не узнаешь. А стоимость может меняться в значительных пределах, на несколько порядков. Закрытие кредита также имеет цену, заранее неизвестную. И если ты не страхуешься: не сохраняешь результаты предыдущих сделок и не резервируешь часть кредита на его гарантированное закрытие, то потеряешь всё.

 

Выполнение тяжёлого фрагмента кода может оказаться неподъёмным, поэтому его следует дробить на куски помельче, проверяя оставшийся лимит после выполнения каждого из них. Но дробить код на совсем крохотные куски тоже не имеет смысла, т.к. проверка оставшегося лимита хотя и дешева, но тоже не бесплатна.

 

Какую часть кредита использовать на выполнение целевого кода, какую потратить на проверку текущего баланса и фиксацию промежуточных результатов, а какую оставить в резерве на непредвиденное повышение стоимости финальных операций — зависит от характера задачи и конкретного сервера.

 

Но если программисту приходится регулярно задумываться над этим вопросом, то что-то не так либо с сервером, либо его задачами, потому как сервер Майнкрафта не предназначен для тяжёлых вычислений. В первую очередь от тяжёлых вычислений надо постараться избавиться. Во вторую очередь тяжелые вычисления должны быть оптимизированы. И лишь в третью очередь следует подумать, как побороть TLWY.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...