ECS
Гуру-
Публикации
533 -
Зарегистрирован
-
Посещение
-
Победитель дней
203
Тип публикации
Блоги
Профили
Форум
Багтрекер
Магазин
Все публикации пользователя ECS
-
Вероятно, для соответствия "ванильной" версии луа
-
В дополнение к инфе от @Bs0Dd поясню, что если ты кодишь под биос, то работаешь на самом низком уровне, доступном в моде. Следовательно, фича component.имяКомпонента.метод(...) из OpenOS доступна не будет, т.к. она добавляется на более позднем этапе во время инициализации самой ОС: https://github.com/MightyPirates/OpenComputers/blob/af2db43c53b9690fceabfb813987572bf2258db5/src/main/resources/assets/opencomputers/loot/openos/boot/04_component.lua#L10-L32 Поэтому для обращения к компоненту из-под биоса есть 2 варианта: -- Чтобы обратиться к компоненту, нам нужно знать его уникальный адрес -- Список адресов всех доступных компонентов с указанным типом можно получить через метод component.list("имя") -- В одном компьтере может быть установлено несколько компонентов одного типа, поэтому метод list -- возвращает функцию-итератор, при каждом вызове которой возвращается адрес следующего компонента local gpuIterator = component.list("gpu") local gpu1Address = gpuIterator() local gpu2Address = gpuIterator() -- Конструкцию выше можно скомпоновать в цикл for address in component.list("gpu") do -- Делаем что угодно с каждой имеющейся GPU end -- В случае EEPROM все несколько проще. Поскольку в 1-ом компьютере может быть только 1 EEPROM, то нам -- следует вызвать функцию-итератор хотя бы 1 раз, чтобы получить адрес 1-го (и единственного) EEPROM local eepromAddress = component.list("eeprom")() -- Раз адрес компонента EEPROM нам известен, теперь мы можем обратиться к нему и вызвать -- какой-нибудь метод. Сделать это можно двумя путями. Первый более линейный, его следует -- использовать для каких-то разовых операций "в лоб": local data = component.invoke(eepromAddress, "getData") component.invoke(eepromAddress, "setData", "sample text") -- Второй более универсальный, и позволяет создать так называемый прокси компонента, то есть -- таблицу в памяти, содержащую все доступные методы компонента. Разумеется, он расходует больше ОЗУ, -- однако работать с ним в разы удобнее: local eepromProxy = component.proxy(eepromAddress) local data = eepromProxy.getData() eepromProxy.setData("sample text") Кроме того, "чистые" компьютеры без ОС имеют глобальные переменные component, computer и unicode, которые всегда доступны в биосе, и в случае OpenOS "вырезаются", становясь частью глобальной библиотеки package: https://github.com/MightyPirates/OpenComputers/blob/af2db43c53b9690fceabfb813987572bf2258db5/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua#L94-L112 По этой причине в биосе ты должен обращаться к глобальной библиотеке component, а в OpenOS для этого уже требуется конструкция require("component"). Почему авторы так поступили? Да хрен знает, их ОС - их правила. Но путаницу это вносит знатную
-
Нет, для мода, конечно, не требуется. Я на всякий случай подчеркнул этот момент - вдруг какая-то заблудшая душа будет искать ответ и не поймет, какого ж черта os.clock() - os.clock() может выдавать что-то, отличное от нуля. И будет плакать... А, вон какая логика была. Занятно, спасибо
-
Черт, стыдно стало, поправил диапазон :d А тут начинается le classique, то есть выбор между производительностью и охватом всех вероятных условий и ошибок. По ТЗ, формируемому названием функции, - да, должна, а ещё ей по-хорошему не хватает парочки валидаций. Начнем с того, что количество цифр в генерируемом числе не должно быть < 1, ибо таких чисел не существует. Также оно не может быть больше 18 для 64-битного бинарника Lua, т.к. именно 18 является максимально допустимой степенью показательной функции f(x) = 10 ^ x, значение которой не превышает рабочий диапазон. Пруф: math.log(math.maxinteger, 10) > 18.964889726831 math.random(10 ^ 18) > 902800305889519796 math.random(10 ^ 19) > stdin:1: bad argument #1 to 'random' (number has no integer representation) И наконец, можно добавить поддержку генерации нуля в крайне странном случае, когда кто-то решил воспользоваться нашей функцией вместо math.random(0, 9): local function fixedLengthRandom(digits) assert(digits >= 1 and digits <= 18, "Digit count is out of range [1; 18]") if digits == 1 then return math.random(0, 9) end digits = 10 ^ (digits - 1) return math.random(digits, digits * 10 - 1) end Это что касается решения "по бумажкам в лабораторных условиях". На практике же оно на фиг никому не упало, и куда предпочтительнее был бы производительный вариант без избыточных проверок, которые имеют свой бюджет вызовов в опенкомпах. Так что же правильнее? Наверное, то, что лично ты считаешь правильным для конкретной ситуации
-
Идея с clock хорошая, но будет работать только в рамках мода из-за хука на вызовы функций в machine.lua. А как ты выбрал магическую константу? Это явно не math.mininteger/maxinteger, а что-то чернокнижное
- 41 ответ
-
- 1
-
-
Прогнал, скопипастив из 2 примера. Спасибо, исправил
-
Оставлю свои пять копеек для тех, кому нужно генерировать рандомные числа фиксированной длины. Хотя к теме это относится опосредованно, но мало ли: -- Вариант 1 local function fixedLengthRandom(digits) digits = 10 ^ (digits - 1) return math.random(digits, digits * 10 - 1) end -- Вариант 2 local function fixedLengthRandom(digits) digits = 10 ^ (digits - 1) return math.floor(digits + math.random() * (digits - 1)) end Вместо math.floor для ускорения процесса можно использовать операцию целочисленного деления // 1.0, однако она сохраняет нулевую дробную часть в результате и доступна только в Lua 5.3. На ваше усмотрение, короче. Результат: >> fixedLengthRandom(4) >> 4981 >> fixedLengthRandom(8) >> 15896813
- 41 ответ
-
- 1
-
-
Господни помидоры! 4к строчек для одной проги, УВОЖАЮ
-
Хех, да я сам хз, для чего она нужна. Я её как основу гуишную юзаю, чтоб всякие асинхронные мониторилки для реакторов/крафтилен на базе по-быстрому писать, что как раз близко к теме автора. Вообще и на опеноси было норм, но поддерживать постоянные изменения в либах из версии к версии я задолбался, поэтому проще стало написать отдельную ось, чтоб самому удобно было. Прошу понять и простить)0
-
Согласен, в майносевском примере я так и поступил. Для опеноси хотелось более простой пример привести, мало ли логическая цепь не особо ясна будет
-
Технически в опенкомпах нет потоков, и все ОСи могут лишь софтверно "скакать" с одной операции на другую, периодически блокируя друг друга и выполняя таски по списку. То есть тут всё блокирующее. Если тебя устроит просто отображать TPS раз в N сек, то заюзай таймер. Код для опеноси: local event = require("event") local computer = require("computer") local component = require("component") local gpu = component.gpu -- Возвращает кол-во реальных (не игровых) наносекунд, прошедших с 00:00:00 01.01.1970 до момента изменения файла на диске local function getRealTimestamp() -- Получаем виртуального компонента filesystem, где можно создавать временную помойку local proxy = component.proxy(computer.tmpAddress()) local path = "timestamp.tmp" -- Создаем временный файл proxy.close(proxy.open(path, "wb")) -- Получаем дату изменения local lastModified = proxy.lastModified(path) -- Удаляем его proxy.remove(path) return lastModified end -- Стартуем фоновый таймер, обновляющийся раз в 1 сек local interval = 1 local lastModified = getRealTimestamp() event.timer( interval, function() -- Примерное значение TPS рассчитывается как расхождение между желаемым интервалом в 1 сек и фактическим, -- полученным через разницу в датах изменения временного файла local tps = interval / (getRealTimestamp() - lastModified) * 20000 -- Выводим значение TPS на экран local oldForeground = gpu.getForeground() gpu.setForeground(0xFFFFFF) gpu.set(1, 1, "TPS: " .. math.floor(tps + 0.5)) gpu.setForeground(oldForeground) -- Все операции вывода на экран занимают время, лучше обновить переменную ещё раз для точности lastModified = getRealTimestamp() end ) Профит: И для майноси: local computer = require("Computer") local component = require("Component") local GUI = require("GUI") local system = require("System") -- Функция та же local function getRealTimestamp() local proxy = component.proxy(computer.tmpAddress()) local path = "timestamp.tmp" proxy.close(proxy.open(path, "wb")) local lastModified = proxy.lastModified(path) proxy.remove(path) return lastModified end -- Добавляем окошко программы в UI local workspace, window, menu = system.addWindow(GUI.filledWindow(1, 1, 60, 20, 0xE1E1E1)) -- Создаем текстовый виджет по центру окошка local layout = window:addChild(GUI.layout(1, 1, window.width, window.height, 1, 1)) local text = layout:addChild(GUI.text(1, 1, 0x4B4B4B, "Тут будет TPS")) -- Вместо event.timer юзаем computer.uptime local uptime = computer.uptime() -- С файликами история та же local lastModified = getRealTimestamp() local interval = 1 text.eventHandler = function(workspace, text, e1, ...) -- Вычисляем, сколько сек прошло с последнего события local deltaTime = computer.uptime() - uptime -- Если прошло больше ожидаемого интервала if deltaTime > interval then -- Вычисляем TPS local tps = deltaTime / (getRealTimestamp() - lastModified) * 20000 -- Обновляем текстовый виджет text.text = "TPS: " .. math.floor(tps + 0.5) text.width = #text.text workspace:draw() -- Обновляем переменные lastModified = getRealTimestamp() uptime = computer.uptime() end end workspace:draw() Результат:
-
Еще API компонента не подразумевает буферизации IO-операций, поэтому слишком частые мелкие обращения к компоненту типа fs.write(handle, "1") будут ощутимо медленнее, чем буферизированная обертка из OpenOS. Т.е. если ты прогаешь какой-нибудь EEPROM с сохранением бинарных файлов побайтово, то добавление строкового буфера хотя бы в 1 Кбайт нифигово апнет скорость
- 3 ответа
-
- 5
-
-
Если юзаешь одну общую видяху, то можно дописать аргумент gpu.bind(адрес, false), чтобы не сбрасывалось разрешение целевого моника
- 4 ответа
-
- 3
-
-
Отключаешь обои - и вуаля, хватает 2 планок оперативы за глаза, только если не запускать какое-нибудь 3D. Чистая оська в минимальной конфигурации требует ~600 кбайт доступной памяти, и я не сказал бы, что это прям лютые и неадекватные затраты: Такова цена граф. интерфейса в изначально консольной среде: тут уже хз, как извернуться, чтобы "скукожить" граф. буфер, жрущий минимум 400 кбайт, словно избалованный толстый кис. Остальные 200 кбайт объедков достаются юишным объектам, либам и буферам I/O. В целом опенкомпы по концепции не предназначены для подобных юишных извратов, поэтому смиренно жрем, чо дали
-
Ну-ка, ну-ка, какая ее часть тебе кажется недостаточно оптимизированной и почему?
-
Нет, офк. Я ж скинул аналогичный результат на чистом консольном луа той же версии, так чего на разность модов-то грешить? Загвоздка явно в поведении железяк. Но боюсь, я абсолютно некомпетентен в этом вопросе, чтобы сгенерировать какие-либо выводы. Цпуинфа: 8 model name : Intel(R) Xeon(R) Silver 4214R CPU @ 2.40GHz
-
Согласен, это так. Но... только в рамках бенчмарка синтаксически различных участков кода на одной машине. Тут, вне сомнений, Lua VM царь и бог, и на результат скорее повлияют локальные особенности трансляции в байт-код, нежели пара лишних операций в исходнике А как быть в нашем случае, когда эквивалентный код под одной VM на различном железе выдает различную производительность?
-
Железо скорее. Луа-консоль запускал на рабочей вдске, сервак кубача поднимаю там же, т.к. сингловый клиент на дешманском ноуте умирает с TLWY даже на 1млн итераций, не говоря уже о 20 млн. С точки зрения дилетанта могу предположить, что наибольшее влияние на однопоточные вычисления оказывает набор инструкций ЦП, объем кеша и частота. Возможно, надо байт-код для каждого метода и каждой машины чекать, чтобы сказать наверняка (он вообще может отличаться для одинаковых версий луа?)
-
OpenComputers-MC1.12.2-1.7.5.192.jar
-
Интервал зависит от кол-ва итераций, а оно такое же, как в примере Zer0Galaxy. Тестил я на десктопном некомпилируемом Lua 5.3.1, лень кубач было ставить. Не нравится? Да пожалуйста, вон результат на 2e7 итераций: Constant 1.152 Upvalue 1.375 math.floor 1.749 floor (no indexing) 1.425 Lua 5.3+: num + 0.5 // 1 1.524 Lua 5.2+: num - num % 1 1.733 И на опенкомпах: Один фиг победа за первым методом
-
Чекнул дополнительно пару известных вариантов округления. Топикстартеру респект за наиболее производительное решение: Constant 0.017 Upvalue 0.023 math.floor 0.027 floor (no indexing) 0.025 Lua 5.3+: (num + 0.5) // 1 0.022 Lua 5.2+: num - num % 1 0.026
-
Дык попробуй отослать MOTD\r\n или MOTD\n, в 90% случаев серваки должны адекватно проинтерпретировать сообщение, если это прям не лютый кастом
-
Сокет-сервак в режиме ресивера работает по накопительному принципу, и сообщения крайне часто будут стоять в буфере друг за дружкой, ожидая обработки, эт нормально. Высокоуровневые библиотеки решают эту проблему через протоколы прикладного уровня по OSI, а ты работаешь с "чистым" сокетом, поэтому придётся минимально пораскинуть мозгами Решений несколько: можно на клиенте перед отсылкой пакета добавлять в начало байт-префикс с длиной содержимого, а на сервере читать накопившийся результат, основываясь на принятых длинах пакетов. Если этот вариант слишком сложен или избыточен (например, если твой софт передает преимущественно короткие сообщения-команды), то можно попросту разделять пакеты любым спец. символом - обычно в сокет-чатах используют символ перевода строки \n
