Перейти к содержимому
Fingercomp

Спустя пару дней выпустили версию OC 1.7.7. В основном фиксы регрессий из этой версии.

Сообщение добавил Fingercomp

OpenComputers 1.7.6

Fingercomp

1 115 просмотров

Новая версия OpenComputers. Неожиданно.

 

Из наиболее интересного:

  • Видеобуферы у графической карточки.
    • Помимо основного, нулевого буфера, который отображается на экране, теперь можно аллоцировать дополнительные буферы — 2D-массивы символов с заданным разрешением, с которыми можно проводить те же операции, что и раньше: set, fill, copy и т. д., — но без потребления бюджета вызовов (то бишь халявно).
    • Добавлена операция bitblt (bit blit), которая копирует кусок одного буфера на другой. Копирование на основной буфер потребляет бюджет вызовов пропорционально разрешению исходного буфера (не размеру области копирования). Может занять несколько тиков.
    • Если верить @ECS, последнее преимущества в производительности практически убивает. Впрочем, за несколько лет, пока буферы висели в дев-билдах, люди уже их заиспользовали для игрушек: вот платформер, например.
    • Прямые вызовы методов компонентов (любых, не только GPU), не имеющих явных лимитов или использования бюджета вызовов, теперь абсолютно бесплатны с этой точки зрения. Раньше они потребляли одну тысячную единицы бюджета вызовов. Подробнее о них — в моей статье.
  • Обновлён шрифт: покрытие значительно расширилось путём забития недостающих символов глифами из Unifont.
  • Заблокирован диапазон 0.0.0.0/8 для интернет-карты. Запросы туда делают примерно то же, что запросы на localhost. Примечательно, что эту уязвимость использовали на CTF для обхода файрволла. Советую почитать.
  • Метод media добавили и для дисководов в серверной стойке. Досадное упущение.
  • Пофиксили отключение компов при перезагрузке чанка: в определённых случаях стейт компьютеров вовсе не сохранялся, из-за чего они рестартились при выгрузке и подгрузке чанка.
  • Починили debug.sendToDebugCard (весьма полезная функция).
  • Разобрались в ориентации редстоун-карт. В 1.7.3 карточки в компах и серверах почему-то использовали абсолютные направления (север/юг/запад/восток) вместо относительных.
  • Беспроводные модемы первого уровня снова могут получать сообщения. А раньше не могли. Это была бага.
  • Ретрансляторы потеряли возможность ретранслировать на неограниченно большие расстояния. В коде мода запутались в min и max.
  • В результатах поиска вейпоинтов навигационным апгрейдом теперь пишутся их адреса.

 

Остальные изменения и ссылка на скачивание — на GitHub.

  • Нравится 6
  • Одобряю 1


22 комментария


Рекомендуемые комментарии

Ого, обновление!

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

А вот скорость bitblt откровенно удручает.

Ждём, когда добавят блиттер!

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

Поделиться комментарием


Ссылка на комментарий

Да ладно бы эти видеобуферы! :)

Наконец компы на сервере перестанут выключаться не по делу - вот это да! И расширение шрифта - тоже неплохо.

Поделиться комментарием


Ссылка на комментарий

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

 

23N10JP.gif

 

Плюсы: расход RAM упал до 25% на полностью инициализированную систему с загруженными (но не отрисованными) иконками. Для слабых машин это очень хороший показатель, и освободившиеся ресурсы можно было бы направить на прикладной многозадачный софт.

 

Минусы: оно лагает сильнее, чем софтверное решение с графоном!11

 

Но, будем честны, я натягиваю сову на глобус. Концептуально фича крайне сочная, API удобное и не ломает старые проекты, написанные под прямую работу с GPU. Думаю, она будет идеальным решением для узкозадачных юишных софтин типа мониторилок реакторов, кнопочных контроллеров для умного дома, текстовых чатов или рисовалок - иными словами, для всего, что могло слегка подлагивать из-за прямой отрисовки. И если раньше надо было извращаться с порядком операций, экономя каждый тик, то теперь можно кодить гораздо комфортнее. Хотя мне все равно чуточку обидно. Но лишь чуточку.

Поделиться комментарием


Ссылка на комментарий
1 час назад, Anon сказал:

Скорее `сломан брайль`

Уже пофикшено, будет в релизе 1.7.7.

Правда, есть подозрения, что там просто откатили новый шрифт целиком.

Поделиться комментарием


Ссылка на комментарий
12 часа назад, ECS сказал:

Плюсы: расход RAM упал до 25% на полностью инициализированную систему с загруженными (но не отрисованными) иконками. Для слабых машин это очень хороший показатель, и освободившиеся ресурсы можно было бы направить на прикладной многозадачный софт.

 

Минусы: оно лагает сильнее, чем софтверное решение с графоном!11

К слову, насколько сильно будет тормозить решение, которое перенесёт массивы-буферы из луа в vram, но склеивание операций и рендер будет проводить всё так же в луа, не используя bitblt?

Поделиться комментарием


Ссылка на комментарий
6 часов назад, Fingercomp сказал:

К слову, насколько сильно будет тормозить решение, которое перенесёт массивы-буферы из луа в vram, но склеивание операций и рендер будет проводить всё так же в луа, не используя bitblt?

Идея занятная, но тормозит люто. Подправил либу, после склейки дело дошло до рендеринга лишь спустя ~5 сек, то есть комп фактически виснет на процессе поиска пикселей со схожими цветами на тоннах вызовов setActiveBuffer/get. У меня вообще зародилось смутное подозрение, что все операции над буферами ни разу не zero cost даже с поправкой на искусственный троттлинг хука вызовов в machine.lua. И ладно бы вывод на экран - сам процесс рисования в буфер через setBackground/setForeground/set явно кушает бюджет. Как говорится, численно доказать не могу, но эмпирически чувствую

Поделиться комментарием


Ссылка на комментарий
14 минуты назад, ECS сказал:

Идея занятная, но тормозит люто. Подправил либу, после склейки дело дошло до рендеринга лишь спустя ~5 сек, то есть комп фактически виснет на процессе поиска пикселей со схожими цветами на тоннах вызовов setActiveBuffer/get. У меня вообще зародилось смутное подозрение, что все операции над буферами ни разу не zero cost даже с поправкой на искусственный троттлинг хука вызовов в machine.lua. И ладно бы вывод на экран - сам процесс рисования в буфер через setBackground/setForeground/set явно кушает бюджет. Как говорится, численно доказать не могу, но эмпирически чувствую

Возможно, не бюджет расходуется дико (насколько я понял, каждый прямой вызов в побочный буфер потребляет 2-31 единиц бюджета), а оверхед просто от вызовов доминирует. Кроме того, там можно выделять не два буфера по 160×50, а один на 160×100, например: число вызовов setActiveBuffer вдвое станет меньше. Хотя с таким оверхедом, видимо, не особо это поможет...

Поделиться комментарием


Ссылка на комментарий
1 час назад, Fingercomp сказал:

Возможно, не бюджет расходуется дико (насколько я понял, каждый прямой вызов в побочный буфер потребляет 2-31 единиц бюджета)

Разве это не смехотворно малая величина? В таком случае от бюджета серверной стойки в полной комплектации в 1.4 единицы должно оставаться 1.4 - 160 pix * 50 pix * (2 setActiveBuffer + 2 get) * 2-31, то есть 1.3999850988387

 

Хорошо, предположим, остаток полностью съедают вызовы функций. Пишем для теста любую функцию-заглушку и вызываем её примерно такое же кол-во раз, что и при группировке схожих пикселей:

local function throttle()
  local a = 123
  local b = a + 456
  local c = a + b ^ a
end

function screen.update()
  for i = 1, 160 * 50 * (2 + 2) do
    throttle()
  end
  
  componentInvoke(GPUAddress, "bitblc", 0)
end

И... оно работает с той же скоростью, что и в первом примере. То есть на глаз разница не видна, хотя технически она, конечно, есть. Поэтому, имхо, расход бюджета сильно-сильно больше, а хук на вызовы особой роли не играет. Жаль только, что нет фичи вывода значения бюджета через debug.getCallBudgetValue, чтобы знать наверняка

 

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

Кроме того, там можно выделять не два буфера по 160×50, а один на 160×100, например: число вызовов setActiveBuffer вдвое станет меньше

Хорошая идея. Закодил, чекнул, скорость действительно чуть возросла, но осталась в пределах ~1 кадра в 3 сек. Вообще мы осознанно убрали bitblc в угоду самописному решению, которое явно проигрывает, так что копать дальше в этом направлении смысла мало. Плюс у нас происходит дублирование вызовов setBackground/setForeground: первый раз во время отрисовки основной графики в буфер, а затем второй раз, но уже в сгруппированном по цветам виде при выводе на экран. Бедная видяшечка, на тайваньских майнинг-фермах и то живется легче (9

Поделиться комментарием


Ссылка на комментарий
9 минут назад, ECS сказал:

нет фичи вывода значения бюджета через debug.getCallBudgetValue

Можно попытаться впилить её в machine.lua, если Scala её экспортирует как коллбек.

Только лучше в библиотеку computer впиливать, наверно.

Поделиться комментарием


Ссылка на комментарий
1 час назад, ECS сказал:

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

Честно говоря, не очень понял, что имеется в виду. Поэтому на всякий случай проясню, что имел в виду я.

 

Если замена in-Lua буферов на VRAM привнесла задержки на порядок, то виноват, скорее всего, некто из следующих элементов:

  • Lua. Нативные либы собраны с -O0.... и заметно тормозны. Сюда же пойдёт хук для TLWY.
  • Оверхед из-за интерфейса Lua ↔ Scala (об этом ниже).
  • Бюджет вызовов, который могут потреблять прямые вызовы get на ненулевой буфер.

Каждый прямой вызов, даже если он бесплатный (а он должен быть практически бесплатным, если активен ненулевой буфер), проходит через слой Lua ↔ Scala. Это значит запаковать аргументы, проверить их, передать в сишную функцию от JNLua, которая для каждого аргумента создаст жвм-объект, найти колбэк по адресу компонента и имени метода, сожрать 2-32 единиц бюджета, произвести диспатч в колбэк, внутри него обработать вызов, потом принять результаты, их нормализовать в поддерживаемые JNLua, отдать в нативную сишную функцию от JNLua, которая каждый принятый жвм-объект преобразует обратно в луа-значения и вернёт их, потом полученные значения запаковать, проверить, распаковать и вернуть. Я надеюсь, что даже при таком длинном описании на деле это займёт не особо длительное время (думаю, порядка сотни микросекунд; источник оценки — потолок), однако если произвести 16 тысяч таких вызовов, то оверхед может стать достаточно существенным.

 

Именно про этот оверхед я и говорил. Я предполагаю, что софтверное склеивание тормозит из-за него. Если гадать ещё безбашеннее, то можно сказать, что хук для TLWY вообще практически бесплатен на фоне него. Аргумент против этой гипотезы: ожидаемая задержка (0.5–1.5 секунд) от полученной (3 секунды) отличается разительно.

 

А bitblt действительно пожирает очень много единиц бюджета вызовов. Если точнее, то для копирования из буфера 160×50 на основной гпу третьего уровня сожрёт 1.5 единиц бюджета (то есть практически точно положит комп на тик). Если копировать из буфера, площадь которого больше максимального основного разрешения соответствующего уровня, комп вообще будет спать несколько тиков.

 

P. S. В довольно древней версии оцелота есть гпу-буферы в том виде, в каком они оказались в OC 1.7.6, и график бюджета вызовов. Можно протестить прогу там.

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

Поделиться комментарием


Ссылка на комментарий
22 минуты назад, Fingercomp сказал:

Именно про этот оверхед я и говорил. Я предполагаю, что софтверное склеивание тормозит из-за него

Я тоже как раз про него и говорил. И по-прежнему не соглашусь. Заменяешь тело функции throttle из примера выше на любой код уровня Lua ↔ Scala типа unicode.sub("abcdef", 1, 2) или computer.uptime(), вызываешь его 160 * 50 * 2 раз перед выводом графики на экран через bitblt - и ничего не изменится. С позиции наблюдателя никаких микро-задержек не будет, bitblt отработает так же, как и без искусственного замедления, потому что, как ты сам подметил, bitblt скушает в сотни (если не тысячи) раз больше бюджета. А теперь вставляем 16к вызовов gpu.get на ненулевой буфер и наслаждаемся мертвым компом. Поэтому смею предположить, что реальный бюджет обращения GPU к ненулевому буферу сильно значимее 2-32, и практический эксперимент это подтверждает

 

51 минуту назад, Fingercomp сказал:

P. S. В довольно древней версии оцелота есть гпу-буферы в том виде, в каком они оказались в OC 1.7.6, и график бюджета вызовов. Можно протестить прогу там.

Чекнул, ловлю рантаймовую оплеуху. В кубаче работает, так что фиг с ним

 

328493099_-2.jpg.214d14426cb411f5b032600e59ea9810.jpg

Поделиться комментарием


Ссылка на комментарий
2 часа назад, Fingercomp сказал:

Каждый прямой вызов, даже если он бесплатный (а он должен быть практически бесплатным, если активен ненулевой буфер), проходит через слой Lua ↔ Scala. Это значит запаковать аргументы, проверить их, передать в сишную функцию от JNLua, которая для каждого аргумента создаст жвм-объект, найти колбэк по адресу компонента и имени метода, сожрать 2-32 единиц бюджета, произвести диспатч в колбэк, внутри него обработать вызов, потом принять результаты, их нормализовать в поддерживаемые JNLua, отдать в нативную сишную функцию от JNLua, которая каждый принятый жвм-объект преобразует обратно в луа-значения и вернёт их, потом полученные значения запаковать, проверить, распаковать и вернуть. Я надеюсь, что даже при таком длинном описании на деле это займёт не особо длительное время (думаю, порядка сотни микросекунд; источник оценки — потолок), однако если произвести 16 тысяч таких вызовов, то оверхед может стать достаточно существенным.

Надо же, оказывается, существует бюджет и для прямых вызовов! Похоже, что значение бюджета вызовов 232 является чисто номинальным. По факту тяжело сделать даже 216 вызовов computer.uptime за такт. При том, что это сравнительно дешёвый вызов. То есть, совершая прямые вызовы, мы упираемся не в бюджет вызовов, а в производительность, условно говоря, процессора.

 

Поделиться комментарием


Ссылка на комментарий
15 часов назад, ECS сказал:

Я тоже как раз про него и говорил. И по-прежнему не соглашусь. Заменяешь тело функции throttle из примера выше на любой код уровня Lua ↔ Scala типа unicode.sub("abcdef", 1, 2) или computer.uptime(), вызываешь его 160 * 50 * 2 раз перед выводом графики на экран через bitblt - и ничего не изменится.

unicode.sub и computer.uptime — это не методы компонентов, а функции, которые практически напрямую дёргают методы луа и потому несравнимо дешевле. Сравнение нечестное. Проверил в оцелоте: 16к вызовов gpu.get заняли 0.80 секунд (avg 50 μs / call), а столько же computer.uptime — 0.04 (avg 2.5 μs / call). Такое же число вызовов методов других компонентов также занимают около 0.8 секунд. Потребление бюджета вызовов при этом остаётся околонулевое.

Поделиться комментарием


Ссылка на комментарий
4 часа назад, Fingercomp сказал:

unicode.sub и computer.uptime — это не методы компонентов, а функции, которые практически напрямую дёргают методы луа и потому несравнимо дешевле

Я и не утверждал обратного, это уже какая-то нездоровая полемика. Проясню:

 

1) Я продемонстрировал, что комп умирает на несколько сек при обращении к gpu.get, предположив, что обращение к ненулевому буферу ни разу не бесплатное

2) Ты сообщил, что доминирует некий оверхед, а расход бюджета при обращении к ненулевому буферу должен быть 2-31

3) Я ответил, что значение в 2-31 слишком мало, и бюджета после 16к вызовов должно оставаться с запасом, и проблема явно в другом месте

4) Ты пояснил, что имел в виду под оверхедом, упомянув нативные либы, интерфейс Lua <> Scala и сам бюджет вызовов

5) Я не согласился, приведя пример с 16к вызовами unicode и computer, имитирующими оверхед без расхода бюджета, и работающий незаметно с точки зрения производительности. Поскольку из примера был исключен лишь расход бюджета, а работа нативных либ и прокидывания данных через Lua <> Scala сохранены, я сделал вывод, что к тормозам приводит именно бюджет

 

5 часов назад, Fingercomp сказал:

Потребление бюджета вызовов при этом остаётся околонулевое

Разве это не подтверждает тезис о том, что gpu.get кушает сильно больше 2-31 бюджета? Может быть, каждый вызов прямого метода компонента расходует некое константное кол-во бюджета в дополнение к прописанному в @Callback значению?

Поделиться комментарием


Ссылка на комментарий
42 минуты назад, ECS сказал:

16к вызовами unicode и computer, имитирующими оверхед без расхода бюджета

В том и дело, что они оверхед этот не имитируют. Там нет никаких преобразований, упаковок и распаковок. Эти функции напрямую работают со стэком значений, не трансформируя что-либо. Слоёв абстракции сильно меньше.

 

Только методы компонентов — из-за общности интерфейса: как со стороны луа, который позволяет передать любые значения и получить любые значения при вызове любого метода любого компонента, так и со стороны скалы, где трансформируются значения, — влекут за собой проход сквозь кучу обёрток, которые сильно роняют производительность. И только их оверхед я считаю существенным.

 

47 минут назад, ECS сказал:

Разве это не подтверждает тезис о том, что gpu.get кушает сильно больше 2-31 бюджета?

Я вставил дебаг-принты в оцелота в том месте, где изменяется значение оставшегося бюджета вызовов. Каждый get занял 4.656612875245797×10-10 единиц. Это как раз 2-31. Даже миллиард таких вызовов не могут израсходовать бюджет настолько, чтобы компьютер прилёг на один тик. Бюджет — это же просто чиселка, которая уменьшается при каждом прямом вызове и регенится после каждого yield. Какой-либо эффект оно оказывает, только если уменьшается до нуля. При этом во время работы 16к вызовов ниже 1.499999 он даже не опускался.

 

Однако потребление процессорного времени во время прокрутки цикла с 16к гетами стопроцентное.

Поделиться комментарием


Ссылка на комментарий
3 часа назад, Fingercomp сказал:

В том и дело, что они оверхед этот не имитируют. Там нет никаких преобразований, упаковок и распаковок. Эти функции напрямую работают со стэком значений, не трансформируя что-либо. Слоёв абстракции сильно меньше.

 

Только методы компонентов — из-за общности интерфейса: как со стороны луа, который позволяет передать любые значения и получить любые значения при вызове любого метода любого компонента, так и со стороны скалы, где трансформируются значения, — влекут за собой проход сквозь кучу обёрток, которые сильно роняют производительность. И только их оверхед я считаю существенным.

Да я тебя умоляю, мод там что, каждый раз напрямую из NBT данные гоняет по тысячам фабрик с фасадами и кастами, что возникают такие нагрузки? Возьми gpu.getScreen, modem.isWireless или computer.isRunning - это же чисто логически самые стековые методы, прям стековее некуда, а роняют компьютер так же, как gpu.get на ненулевой буфер, хотя в теории они должны выполняться быстро, как computer.uptime. Падазритильна!

 

И о какой общности интерфейса с любыми значениями речь, если вся работа с Eris сводится к push/pop, а все аргументы луа-функций в любом случае обрабатываются как массивы значений? В чем техническая разница реализации методов компонентов и методов computer/unicode на Scala? Ты просто так говоришь, будто обращение к компонентам какую-то тяжелую артиллерию поднимает - вот мне и интересно, что же за тайное знание там сокрыто

 

3 часа назад, Fingercomp сказал:

Однако потребление процессорного времени во время прокрутки цикла с 16к гетами стопроцентное.

А что такое "процессорное время" в рамках работы с компонентами в оцелоте? Суммарное выполнения всех операций относительно длительности тика? Если да, то оно автоматически включает в себя следствие расхода бюджета и сопутствующий оверхед. И если ты численно подтверждаешь, что бюджет практически не расходуется, и если оверхед на практике огромен даже на самых примитивных методах типа isRunning, то значит ли это, что любое обращение к компоненту несет в себе искусственную задержку?

Поделиться комментарием


Ссылка на комментарий

Хочу заметить пару интересных деталей поведения vram-буферов, найденных эмпирическим путём, о которых в документации не сказано:

  • Активные цвета видеокарты (setBackground и setForeground) привязаны к vram-буферу. То есть переключив активный буфер, вы так же переключаете и активные цвета, установленные в этом буфере. Очень удобно, на мой взгляд
  • Палитра цветов (setPaletteColor, getPaletteColor) привязана к vram-буферу, но ведёт себя немного иначе. Ведь палитра цветов, как известно, сохраняется даже при перезагрузке компьютера (Причём, что интересно, привязана к монитору, а не к видеокарте. То есть поменяв местами видеокарты двух компьютеров, не меняя местами мониторы, палитра каждого компа останется своей). И каждый vram-буфер тоже имеет отдельную палитру. Но вот при перезагрузке, сохраняется только палитра экранного буфера (под индексом 0). В любом случае приятно, что буферы имеют личные background, foreground, и palette.

На данный момент, это всё, что я нашёл. Надеюсь кому-нибудь эта информация пригодится

Поделиться комментарием


Ссылка на комментарий
5 часов назад, Anon сказал:

каждый vram-буфер тоже имеет отдельную палитру

Хм, а я даже могу придумать этому применение.

У тир3 видеокарты может быть 160*50*2 экранных буферов размера 1*1 (если используется разрешение меньше 160*50, то на буферы останется ещё больше памяти), у каждого буфера есть возможность хранить 57 байт (16 цветов палитры, 3 байта RGB; цвета текста и фона; палитровые цвета текста и фона в единственном символе; сам этот символ).

В сумме 890 КБ доп. памяти, если кому-то надо :).

Поделиться комментарием


Ссылка на комментарий
Гость
Добавить комментарий...

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

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

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

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

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

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