LINUX.ORG.RU

Предсказуемость математики и луа

 ,


1

1
function hashStr (nome)
	hours, minutes = GetGameTime()
	count1=hours* 3,1415926535
	count2=minutes* 3,1415926535
	count3=count1*count2
	count3=string.sub(count3, 1, 3)
	count3=string.format("%03d",count3)
	hNik=string.byte(nome,1)
	hNik2=string.byte(nome,2)
	hNome=hNik*hNik2
	hNome=string.sub(hNome, 1, 3)
	hNome=string.format("%03d",hNome)
	r1=string.sub(count3, 1, 1)
	r2=string.sub(hNome, 1, 1)
	r3=string.sub(count3, 2, 2)
	r4=string.sub(hNome, 2, 2)
	r5=string.sub(count3, 3, 3)
	r6=string.sub(hNome, 3, 3)
	r=r1 .. r2 .. r3 .. r4 .. r5 .. r6
	return r
end

hours, minutes = GetGameTime() получает текущие час и минуту в формате: 01 22

Скармливаем слово на одном компе - получаем предсказуемо одинаковый результат. Скармливаем на другом компе получаем тоже предсказуемо одинаковый результат, но не такой, как на предыдущем компе. Это как вообще? Данные одинаковые. Ник один и тот же. Время одно и то же. Результат всегда разный. Это вообще законно?! Время возвращается серверное - одинаковое и там и там.

Перемещено Dimez из general

★★★★★

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

Хм.. Непонятно совсем.

function testMem()
	SAtest = {}
	for i=1,1000 do 
		SAtest[i] = {}
		for j=1, 100 do
			x=math.random(1,2)
			if x == 1 then
				SAtest[i][tostring(j)] = 1
			else
				SAtest[i][tostring(j)] = 5
			end
		end
	end
end

Вот я записал сто тысяч строк с цифрами. Это занимает в озу 5мб.

function testMem()
	SAtest = {}
	for i=1,1000 do 
		SAtest[i] = {}
		for j=1, 100 do
			x=math.random(1,2)
			if x == 1 then
				SAtest[i][tostring(j)] = true
			else
				SAtest[i][tostring(j)] = false
			end
		end
	end
end

Делаю так - 5мб.

function testMem()
	SAtest = {}
	for i=1,1000 do 
		SAtest[i] = {}
		for j=1, 100 do
			x=math.random(1,2)
			if x == 1 then
				SAtest[i][tostring(j)] = "Многомного текст, очень много"
			else
				SAtest[i][tostring(j)] = "Еще больше различного текста"
			end
		end
	end
end

Делаю так - все те же 5мб. До килобайта совпадение. Как так?

Но рядом сохраняю логи чата и они жрут до мегабайта в сутки. Я чего то не понимаю в работе памяти.

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

Это общее потребление всех аддонов. Я его только что тоже проверял - все совпадает. Если создать таблицу с записями разница в общем потреблении на 5мб.

То есть он почему то учитывает только структуры, но не содержимое таблицы. Непонятно.

LightDiver ★★★★★
() автор топика
Ответ на: комментарий от ann_eesti
function testMem()
	SAtest = {}
	for i=1,1000 do 
		SAtest[i] = {}
		for j=1, 100 do
			x=math.random(1,2)
			if x == 1 then
				SAtest[i][j] = "Многомного текст, очень много"
			else
				SAtest[i][j] = "Еще больше различного текста"
			end
		end
	end
end

Сделал вот так. Потребление стало 1,7мб.

То есть содержимое таблиц вообще не важно и не учитывается. С ним можно не работать никак. Нужно оптимизировать структуры и тут все сложнее.

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

Это

SAtest[i][tostring(j)] = 1

И это

SAtest[i][tostring(j)] = true

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

SAtest[i][tostring(j)] = "Многомного текст, очень много"

Ситуаций может быть три в некотором приближении

  • Луа переиспользует одинаковые строки, а это значит что
    • Вместо 100000 строк будет 1 55 байтовая строка и 99999 указателей на неё
  • Луа не переиспользует одинаковые строки или именно эту
    • Или переиспользует, но не всегда и не везде.

Ты сделал 1 строку "Многомного текст, очень много" и 99999 указателей на неё. В Lua одинаковые строки переиспользуются, так экономится очень много памяти, но это не закон, мол всегда так, зависит от ситуации/реализации.

Вот потребление памяти в байтах на всех версиях Lua твоих тестов, по по порядку 1==а,2==b,3==c.

dron@gnu:~/Рабочий-стол/tests$ alua a.lua 
☺─────────── luajit ────────────☺
3193895
☺─────────── lua5.1 ────────────☺
5233543
☺─────────── lua5.2 ────────────☺
5221556
☺─────────── lua5.3 ────────────☺
4197922.0
☺─────────── lua5.4 ────────────☺
3171417.0
dron@gnu:~/Рабочий-стол/tests$ alua b.lua 
☺─────────── luajit ────────────☺
3193839
☺─────────── lua5.1 ────────────☺
5233559
☺─────────── lua5.2 ────────────☺
5221108
☺─────────── lua5.3 ────────────☺
4197938.0
☺─────────── lua5.4 ────────────☺
3171417.0
dron@gnu:~/Рабочий-стол/tests$ alua c.lua 
☺─────────── luajit ────────────☺
3194095
☺─────────── lua5.1 ────────────☺
5233716
☺─────────── lua5.2 ────────────☺
5221265
☺─────────── lua5.3 ────────────☺
4198095.0
☺─────────── lua5.4 ────────────☺
3171574.0
dron@gnu:~/Рабочий-стол/tests$

Проверяется память через print(collectgarbage("count")*1024), множим на 1024 для показа байт, а не килобайт.

Но рядом сохраняю логи чата и они жрут до мегабайта в сутки.

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

А так, когда одинаково и/или непрерывно то есть некоторые оптимизации, во всех остальных случаях как бог руку положит учитывая всю динамику жизненного цикла lua программы. Если ты думаешь что некими хитрыми способами сможешь сэкономить мегабайты памяти, то я тебя расстрою, нет не сможешь там уже и так всё упаковано, так как о любой вещи о котрой ты помумаешь в рамках памяти Lua уже подумали, а любую оптимизацию которая была бы универсальна и своим добавлением не давала побочек уже сделали. Правило простое, однородные по типу данные имеющие непрерывную последовательность могут иметь оптимизацию в памяти, одинаковые данные типа string будут переиспользоваться без дублирования памяти, во всех остальных случаях все данные хранятся в хештаблице с её плюсами и минусами. Всё. Про userdata и подобное я молчу, отдельная тема.

Но, во первых, в любом случае относись к словам не как к истине, а так, со скепсисом =))) И ещё в частных случаях может случится так что у тебя потребление памяти упало, бывает это после прохода GC в случае когда ты замеряешь сначала до него, а потом после и бывает в случае когда у тебя данные меняются, меньше дырок, больше непрерывных таблиц, много одинаковых строк, больше числовых ключей, в замен дыркам с нилами, разрозненной «индексации» и смешанными типами в таблицах.

А мегабайт в день из за чата это 365 мегабайт всего чата за весь год, это не много. Можешь например хранить в памяти не более 10000 сообщений, а всё остальное сбрасывать на диск, организовав циклический буффер на стыке которого начало-конец сообщения сбрасываются на диск просто записывая новое сообщение в голову, а самое старое в файл и всё и не будет у тебя никаких проблем с памятью +/- фиксированное значение с учётом того что есть лимит на размер сообщения.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU
function testMem()
	local shablon="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	SAtest = {}
	for i = 1,500 do 
		SAtest[i] = {}
		for j = 1, 50 do
			SAtest[i][j] = ""
			for q = 1, 5 do
				print(string.utf8sub(shablon,math.random(1,117),math.random(1,117)))
				SAtest[i][j] = SAtest[i][j] .. string.utf8sub(shablon,math.random(1,118),math.random(1,118))
			end
		end
	end
end

4790.455078125 кб

function testMem()
	SAtest = {}
	for i = 1,500 do 
		SAtest[i] = {}
		for j = 1, 50 do
			SAtest[i][j] = 1
		end
	end
end

451.4638671875 хммм.. Разница в 10 раз. Интересно.

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

Но заметь, разница ВСЕГО в 10 раз. Хотя в одном случае строки вот такого формата: ъыьДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯabcdefghijklmnдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSФХЦЧШЩЪЫЬЭЮЯabcdefghijklm"

А в другом просто 26 тысяч единиц. Не замечаешь беды?

И мне непонятно сколько занимает бул озу. Пишут, что 16 байт, а цифра по идее 8 байт. Или одинаково?

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

Посмотри как увеличивается память на каждой итерации в байтах, не на каждой итерации идёт увеличение памяти, не на размер данных идёт увеличение памяти, а на чанки например снаяала выделяется 64 затем несколько итераций тишина, затем 128 и опять тишина затем 512 и опять тишина. Это не общий объём памяти, а объём в байтах которые добавились в памяти в каждой из итераций, выделяется кусочек размером в X затем он заполняется и несоклько итераций подряд (для строк особено) память не увеличивавется.

string.utf8sub = utf8.sub

local prev = 0
function testMem()
	local shablon="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	SAtest = {}
	for i = 1,500 do
		SAtest[i] = {}
		for j = 1, 50 do
			SAtest[i][j] = ""
			for q = 1, 5 do
				--print(string.utf8sub(shablon,math.random(1,117),math.random(1,117)))
				SAtest[i][j] = SAtest[i][j] .. string.utf8sub(shablon,math.random(1,118),math.random(1,118))
                collectgarbage("collect")
                local curr = collectgarbage("count") * 1024
                print(curr - prev)
                prev = curr
			end
		end
	end
end

testMem()
collectgarbage("collect")
print(collectgarbage("count"))
local prev = 0
function testMem()
	SAtest = {}
	for i = 1,500 do
		SAtest[i] = {}
		for j = 1, 50 do
			SAtest[i][j] = 1
            collectgarbage("collect")
            local curr = collectgarbage("count") * 1024
            print(curr - prev)
            prev = curr
		end
	end
end


testMem()
collectgarbage("collect")
print(collectgarbage("count"))

Например вот 32 цикла на lua5.4 видно что память выделяется блоками которые каждый раз выделяются всё большие и большие, но не больше 512 за раз и есть много 0.0 это те случаи когда при добавлении строки по ключу, память не тратится, а переиспользуется уже имеющаяся и/или заполняется ранее выделенная.

16.0
32.0
0.0
64.0
0.0
0.0
0.0
128.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
256.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
512.0

С числами же чуть иная ситуация

0
0
56
71
0
324
103
39
107
-34
0
27
0
0
114
0
0
0
80
0
0
98
0
0
0
0
53
0
107

Всё ещё завистит от реализации, например luajit жрёт на 1,5~2 раза больше памяти чем ванильные lua и увидеть это можно только в top/htop, так как он ещё хранить свои jit трассы в памяти, это накладные расходы. И поведение отличается при выделении памяти под строки luajit например кажется никогда не выделяет чанки более 256 байт за раз, в ванили же это 512 потолок и стоит учесть что может не вся выделенная память будет заполнена, строка может быть меньше выделенной ей памяти, но вся память будет зарезервированна этой строкой, это всё уже такие моменты которые не особо-то относятся к программированию на lua это моменты реализации самой lua, даже зная паттерн поведения при увеличении памяти, ничего особо то не сделаешь, ну знаешь ты это, ну и всё, это ничего не даст. Ну, это даст понимание как не мешать lua работать эффективнее, вот и всё.

Опять же, это динамический язык, считай это lisp порой считать что-то такое себе, ну например

local foo = {}

foo[function(x) return x * 2  end] = 1
foo[function(x) return x * 4  end] = 2
foo[function(x) return x * 6  end] = 3
foo[function(x) return x * 8  end] = 4
foo[function(x) return x * 10 end] = 5
foo[function(x) return x * 12 end] = 6
foo[function(x) return x * 14 end] = 7
foo[function(x) return x * 16 end] = 8
foo[function(x) return x * 18 end] = 9

for name,val in pairs(foo) do
    foo[name] = name(val)
    print(foo[name])
end

for name,val in pairs(foo) do
    foo[name] = name(val)
    print(foo[name])
end

Бошку сломаешь, но зачем я это привёл? А затем что модель памяти (если так можно сказать) в Lua рассчитана и на такое и она устроена так чтобы любая шизофрения подобно этой работала хорошо, нет заточки особой под что-то конкретно, должно хорошо работать всё, иначе бы было что на одних типах данных всё летает, а на других тормозит и жрёт память. Есть исключение, это как уже сказал непрерывные данные с неразряженной индексацией x = {1,5,6,7,8,9,5,4,8,115,85,166...} и всё. Ну и копии строк для переиспользования.

Исключение это luajit там очень много подводных камней и знать особенности его работы порой полезно, опять же, не для того чтобы ускорить код, а для того чтобы его не тормозить. А память оно будет жрать, можно сэкономить вызвав jit.off() например. Будет меньше жрать памяти, но медленнее работать.

Не про память, но в догонку

Есть всякие статьи типа lua memory tips and tricks или что-то подобное, но в приципе, и это просто замечательно, там всё довольно скромно и понятно, вариантов мало и в 99% они не про то как «делай вот так чтобы жрало меньше!!!», а про «не делай вот так если не хочешь чтобы не жрало», это лучше потому что по умолчанию всё и так зашибись, но если хочешь побыстрее/поэкономнее не делай вот так, чуть ограничив себя, хотя это с любыми языками сценаприев так, любая фича имеет цену, чем проще фича тем она быстрее, чем проще работа с памятью и чем более однотипна и линейна память тем она жрётся меньше, это просто физическое следствие и оно закон для любого языка, даже если ты сейчас сядешь писать новый язык, ты столкнёшься с неизбежностью, вызовы без доп обработки == быстрее, линейная память с предсказуемым доступом == экономнее, оптимизации по скорости требуют памяти, оптимизации по памяти часто деоптимизируют скорость.

Фух, всё. Я рад что ты пытливо и с интересом залезаешь в кишки, это здорово. Но в целом, чем проще твой код, тем он меньше жрёт и быстрее работает, от и всё. Но тем не менее, тебе наверное уже пора прям исходники твоего интерпретатора изучать =) Я вот не изучал нихера, так пару десятков раз в разные места залезал число глянуть и всё и всё что я узнал, ну вот это работает вот так потому что учитывая все возможные случаи наверное лучшего решения чем реализовано тут уже в природе не существует, и всё =)))

Конечно там есть места которые на вид можно улучшить, но… может это и не так, не всегда в голове держишь все случаи, одно улучшение не должно своими побочными эффектами и накладными расходами ухудшать другое.

Вот теперь точно всё. Ковыряй дальше и вникай глубже, это интересно, делай тесты и замеры и на основе их результатов действуй, так надёжнее ибо динамика зависит от данных, а данные от случая в сумме это будет просто набор правил что вот в такой ситуации с таким набором данных эффективнее вот так, или вот эдак, от и всё =)))) Наверное в целом ты про Lua знаешь уже больше чем я =) Чего я тут расписываю не знаю, но просто мне показалось что ты пытаешься найти грааль чуточку волшебный, который сделает хорошо, ну так-то его нету, но он и не нужен и так всё зашибись, а если не зашибись, значит пришло время писать модуль на Си, но не всегда это возможно значит выкручиваемся по ходу дела и обстановки своими силами как можем, так и живём. The End пуду-пиду-пууууу

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

Просто столкнулся с сильным ростом потребления озу и начал копать.

function addDb(db,key,msg)
	if nsDb == nil then
		nsDb = {}
	end
	if nsDb[db] == nil then
		nsDb[db] = {}
	end	
	if nsDb[db][key] == nil then
		nsDb[db][key] = {}
	end
  	local test
  	local razmerDb = tablelength(nsDb[db][key])
  	if razmerDb > 0 then
	  	for k,v in pairs(nsDb[db][key]) do
	  		if v == msg then
	  			test = 1
	  		end
	  	end
	end
  	if test == nil then
  		local num = tostring(razmerDb+1)
  		nsDb[db][key][msg] = 1
  	end
end

Вот был у меня такой код добавления сообщений в таблицу. Это, кстати, очень серьезный прогресс был, в сравнении с ручными проверками каждый раз и ручным добавлением. И тут до меня дошло - а нахрена?

function addDb(db,key,msg)
  nsDb = nsDb or {}
  nsDb[db] = nsDb[db] or {}
  nsDb[db][key] = nsDb[db][key] or {}
  nsDb[db][key][msg] = 1
end

По сути - то же самое однйо строкой. Без проверок и прочего.

И тут у меня возник вопрос, а что лучше: nsDb[db][key][msg] = 1 или nsDb[db][key][msg] = true

Вот и начал дальше копать.

А теперь у меня вообще общий класс для работы с данными.

https://pastebin.com/Rq7ins8b

Наверное что то такое будет.

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

Ну ты можешь посмотреть на динамику роста памяти (с учётом автоматического вызова GC)

prev = collectgarbage("count") * 1024
function meminfo(msg)
    curr = collectgarbage("count") * 1024
    print(msg ,curr - prev,curr)
    prev = curr
end

function addDb(db,key,msg,sign)
  nsDb = nsDb or {}
  nsDb[db] = nsDb[db] or {}
  nsDb[db][key] = nsDb[db][key] or {}
  nsDb[db][key][msg] = sign
  meminfo(type(sign))
end

print("type","add mem","full mem")

addDb(1,1,"hello",1)
addDb(1,2,"hello",1)
addDb(1,3,"hello",1)
addDb(1,4,"hello",1)
addDb(1,5,"hello",1)
addDb(1,6,"hello",1)
print("-----------")
addDb(1,7,"hello",true)
addDb(1,8,"hello",true)
addDb(1,9,"hello",true)
addDb(1,10,"hello",true)
addDb(1,11,"hello",true)
addDb(1,12,"hello",true)

Динамика увеличения памяти между boolean и number +/- одна. Там где ты видишь отрицательные значения это либо GC пришёл и почистил, либо таблица перестроилась в более оптимальную и пришёл GC и почистил.

dron@gnu:~/Рабочий-стол$ alua a.lua 
☺─────────── luajit ────────────☺
type	add mem	full mem
number	824	42701
number	112	42813
number	128	42941
number	112	43053
number	144	43197
number	112	43309
-----------
boolean	112	43421
boolean	112	43533
boolean	176	43709
boolean	112	43821
boolean	112	43933
boolean	112	44045
☺─────────── lua5.1 ────────────☺
type	add mem	full mem
number	375	32677
number	994	33671
number	194	33865
number	162	34027
number	226	34253
number	162	34415
-----------
boolean	166	34581
boolean	162	34743
boolean	262	35005
boolean	162	35167
boolean	134	35301
boolean	162	35463
☺─────────── lua5.2 ────────────☺
type	add mem	full mem
number	1119	28860
number	251	29111
number	186	29297
number	154	29451
number	218	29669
number	154	29823
-----------
boolean	158	29981
boolean	154	30135
boolean	254	30389
boolean	154	30543
boolean	126	30669
boolean	154	30823
☺─────────── lua5.3 ────────────☺
type	add mem	full mem
number	1095.0	28839.0
number	239.0	29078.0
number	182.0	29260.0
number	150.0	29410.0
number	214.0	29624.0
number	150.0	29774.0
-----------
boolean	152.0	29926.0
boolean	150.0	30076.0
boolean	248.0	30324.0
boolean	150.0	30474.0
boolean	2168.0	32642.0
boolean	151.0	32793.0
☺─────────── lua5.4 ────────────☺
type	add mem	full mem
number	463.0	24333.0
number	158.0	24491.0
number	174.0	24665.0
number	142.0	24807.0
number	206.0	25013.0
number	-930.0	24083.0
-----------
boolean	175.0	24258.0
boolean	142.0	24400.0
boolean	270.0	24670.0
boolean	142.0	24812.0
boolean	112.0	24924.0
boolean	142.0	25066.0
dron@gnu:~/Рабочий-стол$ 

А дело тут не в булах и числах, а в том что у тебя сложные таблицы, сообщения хранятся как ключи. память увеличивается не от булов/чисел, а от таблиц в таблицах, на них же тоже память нужна, например, будем вообще ничего не писать, вставляя nil

addDb(1,1,"hello",nil)
addDb(1,2,"hello",nil)
addDb(1,3,"hello",nil)
addDb(1,4,"hello",nil)
addDb(1,5,"hello",nil)
addDb(1,6,"hello",nil)
print("-----------")
addDb(1,7,"hello",true)
addDb(1,8,"hello",true)
addDb(1,9,"hello",true)
addDb(1,10,"hello",true)
addDb(1,11,"hello",true)
addDb(1,12,"hello",true)
☺─────────── lua5.4 ────────────☺
type	add mem	full mem
nil	408.0	24278.0
nil	134.0	24412.0
nil	150.0	24562.0
nil	118.0	24680.0
nil	182.0	24862.0
nil	118.0	24980.0
-----------
boolean	-1022.0	23958.0
boolean	144.0	24102.0
boolean	270.0	24372.0
boolean	142.0	24514.0
boolean	142.0	24656.0
boolean	112.0	24768.0

А один фиг память увеличивается каждый раз при добавлении в базу на примерно 150~200 байт, ушло просто на накладные расходы от nsDb[db][key] = nsDb[db][key] or {} и подобного, на фоне этого булы и числа как значения просто теряются, сотня байт уходит просто на создание таблицы и создания её ссылки в другой таблице ещё до момента внесения туда полезных данных в виде сообщения как ключа уже другой таблицы которая будет хранить 1 или true =)

А если ещё при каждом вызове функции делать collectgarbage() то результаты ещё поменяются чуток, особенно в начале, а потом уже нет.

Просто есть ещё момент, например некая таблица в начале может строится оптимиально, но потом данные ей не позволят оной быть она перестроится, или наоборот неоптимальная таблица будет перестроена, но вот к последним словам относить ОЧЕНЬ осторожно, я сам не смотрел пока исходники и лишь про это читал на lua-users так что могу врать. Но это видно по поведению GC, если однородные данные начать заполнять разнообразной хренью, ну не может оно больше быть оптитмизированно, значит таблица будет перестроена, создана новая обычная и придёт GC хлопнет прошлую, и ведь приходит же =) Так что я в это верю, но так-то надо смотреть исходники, опять же, все исходники всех версий, и сопоставить некое общее правило, а не какой то частный случай. Я пока к такому не готов да и не хочу конкретно это делать, так как к языку это мало отношения имеет, это вопрос чисто реализации, я например молчу про то что есть lua для браузера, а там вообще под капотом js хоть и написанный например в стиле прямого переписывания С кода на JS как в fingari, но один фиг со своими приколами.

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

https://ctxt.io/2/AAB4QWUpFg

Ууууфф.. Я работал над этим где то неделю, но кажись это шедевр.

Два класса. Один содержит в себе или/и создает ключ в базе данных. Второй на основе этого класса работает с таблицей:

  1. Можно добавлять уникальные данные в таблицу, которые не могут дублироваться
  2. Можно создавать не уникальные дублирующиейся записи
  3. Можно модифицировать данные точечно
  4. Можно удалять записи
  5. Можно перемещать записи по таблицам с переиндексированием всей таблицы
ns_data_base = {} -- Создали общую базу данных
wiki = create_table:new(ns_data_base, "wiki") -- Создали экземпляр "Вики"
wiki_obj = NsDb:new(wiki:get_table(), "ГС",1) -- Создали в таблице подтаблицу с термином "ГС, в котором все данные уникальны
wiki_obj:add("Гс это гс") -- Сделали запись в термин

Каеф то какой, ты не представляшь. Создал абсолютно чистый новый аддон, переписываю все, заранее несколько раз продумывая последствия. С нуля.

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

Затем чтение из базы тоже через этот метод… И можн начинать уже реально работать будет.

Для уникальных записей в таблицах сделал свою систему индексации. Словарь то индексациин не имеет) А у меня - имеет!

https://docs.google.com/document/d/1bJg5cMS6sHgJdtB0AOO0sLOncPz08EjgOiYks6S5zeg/edit?usp=sharing

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

Не могу найти инфу, какой максимальный размер таблицы в луа5.1?

Сколько элементов максимум я могу впихнуть в таблицу?

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

Если ограничение есть, придется делать класс работающий с несколькими такими таблицами и автоматом создающий новую…

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

Хм.. Экспериментально я столкнулся с двумя ограничениями.

  1. невозможно создать больше 524288 элементов таблицы

  2. WTF\Account\VLADGOBELEN\SavedVariables\NSQC.lua:307044: main function has more than 262143 items in a constructor

выдает такую ошибку…но все работает

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

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

А теперь совсем непонятно…

function test1(num, num2)
    testQ['test'] =  {}

    if not num2 then
        for i = 1, num do
            local index = math.floor(i / 500000) + 1  -- Вычисляем индекс на основе i
            testQ['test'][index] = testQ['test'][index] or {}  -- Инициализируем подтаблицу, если она не существует
            testQ['test'][index][i] = "1"  -- Заполняем подтаблицу значением "1"
        end
    else
        local index = math.floor(num / 500000) + 1  -- Вычисляем индекс на основе num
        testQ['test'][index] = testQ['test'][index] or {}  -- Инициализируем подтаблицу, если она не существует
        table.insert(testQ['test'][index], "1")  -- Вставляем "1" в подтаблицу
    end
    print(tablelength(testQ['test']))
    for i = 1, tablelength(testQ['test']) do
        print(tablelength(testQ['test'][i]))
    end
end

Сделал функцию теста типа. test1(762143)

Теперь лимит почему то такой:

499999 в первой таблице и 262144 во второй. Вот где логика?

хмм.. А при ограничении на табица 200к, регает сколько угодно:

[27:36]60
[27:36]199999
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:36]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:37]200000
[27:38]200000
[27:38]200000
[27:38]200000
[27:38]200000
[27:38]200000
[27:38]200000
[27:38]200000
[27:38]200000
[27:38]200000
[27:38]162145
LightDiver ★★★★★
() автор топика
Последнее исправление: LightDiver (всего исправлений: 1)
Ответ на: комментарий от LightDiver

Сейчас нет времени расписывать, но

Ограничение на память в ванили нет 64 битная Lua сожрёт всю память на 64 битной машине и 32 битная все 4 гига на 32 битной (если без хаков). Особняком стоит luajit из за особенностей хранения указателей в версии 2.0 не более 2 с копейками гигабайт, в 2.1 побольше, но это luajit он по идее не совсем Lua, короче с ним всегда и везде костыли. Есть ограничение на количество локальных переменных, смотри конфиг сборки для каждой версии. На количество элементов в таблицах лимита нет, есть на память в целом, чаще всего лимит ограничен лишь битностью ПК, с учётом вышесказанного в виде дополнительных ограничений.

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

Если ты заметил что в Lua который встроен в программу есть какие либо лимиты, или что-то не работает так как ты хочешь и так как описано в

То смирись с этим, сделай себе пометку что вот в таких случаях, вот так вот и всё.

Теперь касаемо кода, ты хоть бы дебагал принтами =)))

Начали
 ...
+ 499996	1
+ 499997	1
+ 499998	1
+ 499999	1 -- сработала это -> local index = math.floor(i / 500000) + 1
- 500000	2 -- та-дааааа, прыг на другой ибо `i > 500000` +1 == index == 2
- 500001	2 -- и теперь мы начали заполнять другое уже
- 500002	2
- 500003	2
- 500004	2
...
- 762143	2 -- и дошли до заветного размера test1(762143)
                -- только вот он через твой прикол с `index` размазался по двум таблицам
                -- а 200000 < 500000 и поэтому все значения в 1 таблице, а не двух.  
Кончили

Я вообще не понял зачем вычислять index, если у тебя есть террабайт памяти, то на

local memory = { }
for i=1,1000000 do
    memory[i] = {}
    for j=1,1000000 do
        memory[i][j] = 1
    end
end

Неспеша сожрёт всё, вгонит в своп и поставит раком :)

Но ничто не мешало разработчикам игры встроить ограничение, хоть на уровне Си, хоть на уровне Lua через метатаблицы проверять размеры таблиц и потребление памяти и писать тебе предупреждение или ваще вываливаться с ошибкой. Если ограничения есть, ну значит они нужны в игре, значит нужно просто это учитывать и всё.

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

У меня не луа, а wow lua, здесь что то странное.

ns_data_base = {
  {
    "1", -- [1]
    "1", -- [2]
    "1", -- [3]
  }, -- [1]
  {
    [4] = "1",
    [5] = "1",
    [6] = "1",
  }, -- [2]
  {
    [7] = "1",
    [9] = "1",
    [8] = "1",
  }, -- [3]
}

Видишь три таблица по три записи? Это одинаковые таблицы и одинаковые записи. Ну, просто ему так хочется.

Если хочу записать 500000 записей в таблицы по 100000 в таблицу, он запишет. Но при перезаходе в игру выдаст ошибку: «constant table overflow» и обнулит файл.

Если я захочу записать в одну таблицу, туда можно максимум 524288. Дальше ошибка биг дата. Вообе при разных размерах и количестве таблиц разные ошибки. Мне нужна предсказуемая расшияемая структура. Я не понимать пока логики работы.

Код я уже исправил:

function test1(num, num2, num3)
    ns_data_base =  {}

    if not num2 then
        for i = 1, num do
            local index = tonumber(math.ceil(i / num3))  -- Вычисляем индекс на основе i
            ns_data_base[index] = ns_data_base[index] or {}  -- Инициализируем подтаблицу, если она не существует
            local shablon="абвгдеёжзийк"
            local temp_b = ""
            for q = 1, 3 do
                temp_b = temp_b .. string.utf8sub(shablon,math.random(1,10),math.random(1,10))
            end
            ns_data_base[index][i] = temp_b  -- Заполняем подтаблицу значением "1"
        end
    else
        local index = math.ceil(num / num3)  -- Вычисляем индекс на основе num
        ns_data_base[index] = ns_data_base[index] or {}  -- Инициализируем подтаблицу, если она не существует
        table.insert(ns_data_base[index], "1")  -- Вставляем "1" в подтаблицу
        print('fdsfsda')
    end
    if ns_data_base[1] then
        print(tablelength(ns_data_base))
        for i = 1, tablelength(ns_data_base) do
            print(tablelength(ns_data_base[i]))
        end
    end
end
LightDiver ★★★★★
() автор топика
Последнее исправление: LightDiver (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Кажется я придумал решение. Я сделаю несколько аддонов чисто под базы данны. Без файлов, без ничего. Чисто конфиг с переменной для хранения. В переменной безопасно можно хранить до 250 тысяч записей. Вполне достаточно. Запись по проверке таблиц. Если переполнилась, использовать следущую Все.

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

https://www.lua.org/source/5.1/

dron@gnu:/mnt/STORAGE/ВНЕШНЕЕ/LUA-STABLE/lua-5.1.5$ grep -R "constant table overflow"
src/lparser.c:                  MAXARG_Bx, "constant table overflow");
src/lcode.c:                    MAXARG_Bx, "constant table overflow");
#####################################################################################
dron@gnu:/mnt/STORAGE/ВНЕШНЕЕ/LUA-STABLE/lua-5.1.5$ grep -R "MAXARG_Bx"
src/lopcodes.h:#define MAXARG_Bx        ((1<<SIZE_Bx)-1)
src/lopcodes.h:#define MAXARG_sBx        (MAXARG_Bx>>1)         /* `sBx' is signed */
src/lopcodes.h:#define MAXARG_Bx        MAX_INT
src/lparser.c:                  MAXARG_Bx, "constant table overflow");
src/lcode.c:                    MAXARG_Bx, "constant table overflow");
#####################################################################################
dron@gnu:/mnt/STORAGE/ВНЕШНЕЕ/LUA-STABLE/lua-5.1.5$ grep -R "MAX_INT"
src/llex.c:  if (++ls->linenumber >= MAX_INT)
src/lopcodes.h:#define MAXARG_Bx        MAX_INT
src/lopcodes.h:#define MAXARG_sBx        MAX_INT
src/llimits.h:#define MAX_INT (INT_MAX-2)  /* maximum value of an int (-2 for safety) */
src/lstring.c:  if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
src/ltable.c:    if (j > cast(unsigned int, MAX_INT)) {  /* overflow? */
src/lparser.c:                  TString *, MAX_INT, "");
src/lparser.c:    luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
src/lparser.c:  luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
src/lcode.c:                  MAX_INT, "code size overflow");
src/lcode.c:                  MAX_INT, "code size overflow");
dron@gnu:/mnt/STORAGE/ВНЕШНЕЕ/LUA-STABLE/lua-5.1.5$

Они задали свои значения ограничений и всё. Пытаться вычислить его явное значение я не буду, лень, 524288 это в 3 байта взалит, игра наверное 32 битная ещё и прочее прочее.

Не влезает у тебя всё в память, я выше уже писал, делай кольцевой буфер и сливай старые записи на диск. Куда тебе сотни тысяч записей то держать в памяти? =)) Зачем? Опять же и особенно если эти скрипты с хреновойтучей записей зашружаются у игроков то всё правильно, тебе как мододелу дают по рукам ибо нехер =) Это везде так в модах для CS:GO/L4D2 например и прочих, тут лимит, там лимит, это нельзя, то нельзя.

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

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

На сем всё, я суп кушать пошёл, гы

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

Вон я выше уже расписал решение. Да, игра 32битная.

Но я сделаю для начала 10 аддонов-баз-данных. В каждом переменная на 250 тысяч записей. Это для начала 2,5 миллиона строк. Пока хватит, потом можно еще создать, если что. Изи.

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

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

function NsDb:new(input_table, key, is_unique)
    local new_object = setmetatable({}, self)  -- Создаем новый объект и устанавливаем метатаблицу
    -- Инициализация таблицы по ключу
    input_table[key] = input_table[key] or {}

Вот я передаю базу классу. Но что мне мешает сделать это вот так:

--блаблапроверки на размеру таблиц
_G["ns_data_base"..i][key] = _G["ns_data_base"..i][key] or {}

Получается, я просто создаю объект и передаю общее имя базы данных. Класс уже внутри себя делает проверки на размеры таблиц и записывает в нужную.

При чтении он все это учитывает и проверяет все созданные таблицы.. Должно сработать.

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

Это штоль?

О каких они константах?

Особенности байткода, 32 бита размер числа максимальный == битовое поле 6 бит на код операции и 26 бит на индекс обращения.

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

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

Это уже смотри сам, у тебя суть в том что ты разбиваешь одну например огромную таблицу с пол-ляма записей на две по четверть ляма, так ты не упираешься в лимиты, а у же как это делать, это уже вопрос десятый, как тебе удобно так и делай =)

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

Усе пропало, гипс снимают, клиент уезжает.

Моя вторая версия аддона пришла к своему физическому завершению. В своей структуре базы данных она избыточна.

Сегодня все рухнуло. Таблица дошла до предела и обнулилась. Вернул из бэкапа, но через двое суток снова все рухнет.

https://pastebin.com/Bs4cz9dB

Например я хранил данные так. Это ошибка.

Один элемент таблица на одну маленькую букву? На 5? На 100? нет.

Придется делать свою бинарную структуру баз данных: Хранение данных адрессно в строке. Строка в вов луа может быть до 32 тысяч символов, я возьму 25 тысяч на строку(1000 сообщений по 254 символа)…

Допустим:

  1. читаем размер первой таблицы если он меньше 200 тысяч, переносим данные последней строки в кэш-таблицу.

1.1) Если таблица 200 тысяч, читаем следущую.

  1. Смотрим размер таблицы, если он меньше адресов меньше 1000, пишем в последнюю строку и собираем строку заново

  2. Если адресов 1000, создаем новую строку, создаем в ней новый адрес заносим строку в кэш-таблицу, собираем строку.

Получится я сразу сокращаю количество объектов в таблице в 1000 раз. Я кодирую всю инфу, сокращая ее объем.

Если мне нужно таблицы под объекты, как в пасте выше, я просто указываю длину строки не 1000, а 100. В строку влезет 100 объектов по 4 байта. 4 байтов хватит для шифровки 65 миллионов объектов - хватит на все.

В итоге вместо таблица на 100 элементов, у меня строка на 100 объектов по 4 байта и в начале строки блок адресов.

Что я мог упустить? Хмм..Ну должно ж сработать.

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

Не забывай что в Lua строки неизменяемые. А это значит любое изменение строки приведёт к выделению новой, заполнению её старыми данными с новыми изменениями. Это к том что операции над строками в целом дорогие, особенно если как я понял ты собераешься динамически менять тыщи значений в большом блобе, а это значит этот большой блоб будет каждый раз копироваться и уничтожаться при каждом его изменении. Но если его формировать 1 раз, а затем лишь чистать из него значения то будет побыстрее конечно.

Ещё раз, строки в Lua не изменяемые, любое изменение строки - это создание её изменённой копии. Тоесть если ты сейчас упираешься в размеры глубины таблицы, которая работает быстро, то при работе со строками ты можешь упереться в скорость. Классика, либо быстро, но с ограничениями, либо без ограничений, но медленно =)

Но это так к слову, всё имеет свою цену. Наверное ты для упаковки будешь использовать string.pack/string.unpack дабы с точностью до байта всё записывать и читать, или конкатенировать будешь просто, не знаю, но, передпереписыванием проведи серию тестов, а то нахлобучишь пересборку строки ака блоба с данными в цикле по 100500 раз для 100500 значений, а ещё менять сам блов за одну итерацию будешь несоклько раз и так далее и обнаружишь, что всё влезает, но работает ну ооооооооооочень медленно.

Классическая медленная операция в Lua это побайтовый обход строки

for i=1,#str do 
   local value = str:sub(1,1)
   -- что-то тут делаешь
end

Такую конструкцию если запускать её более чем 1 раз для уникальной строки, использовать например довольно дорого, и обычно более выгодно её именно что 1 раз (для уникальной строки) закешировать в таблицу

for i=1,#str do 
   str_buff[#str_buff+1] = str:sub(1,1)
end

А уже когда надо обходить/менять, то обходить таблицу, а не саму строку

for i=1,#str_buff do 
   if str_buff[i] == что-то_там then
     -- что-то_тут
   end
end

Так что сначала проверь на практике задумку в тестах. И ещё момент, любое

str = "hello"
str = str1.." world!"

Приводит к тому что значение «hello» теряет ссылку на себя, а это значит за неё придёт GC, а когда приходит GC то Даниссимо, и пусть весь мир подождёт!!! :D

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

Сейчас у меня так:

tbl = {
    ["объекты] = {
        ['1'] = "tree",
        ['2'] = 'house'
    {,
    ['целостность'] = {
        ['1'] = 990,
        ['2'[ = 550
    }
{

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

Я же хочу так:

tbl = "0202 tree house 990 550"

Что мы видим? Мы читаем первый блок строки: 1006. По байту на адрес. То есть первый адрес 02. Два слова в первом объекте. Добавляем эти слова в кэш таблицу. Смотрим второй адрес - там тоже два слова. Добавляем два слова в следущую таблицу. Это временные таблицы. Они используются для каждого обращения к строкам. Из них же пересобираются строки.

Берем кэш таблицу и формируем из нее строку: в первой таблице три слова стало? Мы ее работали? Ок, тогда tbl = "03

Во второй два осталось? Ок, tbl - "0302

дальше записываем обратно: tbl = «0302 tree tree house 990 950»

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

function mysplit (inputstr, sep)
	if sep == nil then
		sep = "%s"
	end
	local t={}
	for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
		table.insert(t, str)
	end
	return t
end

Допустим, зранение чисто блоковое:

tbl = "1006 tree house 950550"

То есть перввый объект 10 символов, второй объект 6 символов. Но тогда читать стринг.сабом… Хмм

Сервер ребутается раз в сутки, не думаю что даже при частой работе хранящиеся объекты станут большой проблемой.\

Или же по переменной на объект вообще, смотри:

objects = "02 house tree" -- два объекта по 5 символов
hp = "02 950550" -- два объекта по 3 символа

Вопросы лишь - чем обрабатывать лучше…

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

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

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

Тестируй крч, а по результатом замеров выбирай как действовать :)

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

Допустим не надо ничего распаковывать и упаковывать. Прямая работа со сторокой.

str = "1218 Привет   мир   Этовторая фраза"

Вот у нас в строку записано две фразы. Я изначально классу передал, что размер слова всегда 6 символов.

Теперь что мы видим? В первой фразе 12 символов. Каждое слово по 6 символов. Берем размер адреса 4+1. Теперь у нас первое слово: string.utf8sub(str,4+1,4+1+6). Получили

Вторая фраза у нас тут: 4+1+12. Первое словоее тут:

string.utf8sub(str,4+1+12,4+1+12+6)

Вроде нет вычислений. Чистая адрессная посимвольная работа.

Можно даже не буквы считать, а количество слов. Мы же знаем размер слова всегда:

str = "0203 Привет   мир   Этовторая фраза"
LightDiver ★★★★★
() автор топика
Последнее исправление: LightDiver (всего исправлений: 2)
Ответ на: комментарий от LINUX-ORG-RU

Я придумал!!!!

Представь, что у нас строка 20000 символов. В ней конечно же работать не надо. Это дорого и глупо. Поэтому у нас в ей адреса подстрок. А точнее их длины:

str = '1017 привет мирэто вторая строка"

Мы хотим работать со второй строкой. Ее длина 17. Значит мы берем длину адресов+пробел+длину первой фразы - это начало второй фразы. Плюс 17 - это конец второй фразы. И вырезаем из большой фразы эту подфразу всего из 17 символов. И уже с ней работаем. Все.

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

Итак.. Я почти не спал, тестировал, писал, изучал. Реализовал шедевр из шедевров: сжатое префиксное дерево… Реализовал хранение в строках.

А в итоге по тестам оказалось, что вершина кодостроения - хэш-таблицы. Вот как так то?!

Просто разбиваем фразы на слова и добавляем строку в массив элементов. Все. Это идеал.

По размеру эта структура сравнима с префиксным деревом, а вот по времени работы: добавление в хэш-таблицу 63 тысяч строк логов занимает 0.1с, а в дерево 71с… Но я же писал! Я сутки это пилил, как бросать теперь?

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

Как перестать оптимизировать и начать жить? Астанавите меня…

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

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

Расстраиваться не стоит, зато у тебя сейчас нет синдрома выбора, ой а может вот так сделать, ой нет а лучше наверное будет вот так… И так далее, метаешься в панике чисто на эмоциях нравится вот это, а вот то слишком сложное/простое/банальное/жирное/худое/ и тд. Но рано или поздно берёшь и просто смело идёшь по граблям, полбу наполучал и всё понял, типа вот это прикольно, а вот это лучше, а все сомнения были чисто на уровне восприятия, мы часто оцениваем код или даже программу чисто эмоционально, увидал маленькую программу, сразу в ней нащёл 100500 фатальных недостатков ДА Я САМ МОГУ ТАКУЮ СДЕЛАТЬ!!1 И ведь реально делаешь, а потом открыл blender, а там всё сложно, запутанно, непонятно и нужно многое просто держать в голове, но в виду что это монстр, но при этом клёвый вся прыть навешивания фатальных недостатков улетает и ты уже думаешь ага, в это надо просто вникнуть и изучить, а не я ща сам всё по нормальному сделаю!!!, так же и со своим кодом, есть нечто которое можно написать и так и сяк, и пока не сделаешь и так и сяк и не сравнишь будет любимчик сяк или так.

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

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

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

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

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

А со сном не шути, лучше высыпаться. Это на энтузиазме можно несколько месяцев по ночам долбиться с клавой, перебив режимы, сна, еды, и прочего, это прикольно и клёво, но когда пристумами, ну типа загорелось и пару дней херачишь, а потом отпускает и уже не спеша продолжаешь, но если это войдёт в привычку и станет просто нормой, то дело плохо. Будешь часами думать, и тупить, писать 2 строчки, а на утро удалять и снова думать. Короче будет казаться что, что-то делашеь, а не деле ту просто тупой и невыспавшийся кусок мяса, а мозг может только фантазировать и вечно сомневаться.

А в итоге по тестам оказалось, что вершина кодостроения - хэш-таблицы. Вот как так то?!

Я уже писал, в Lua хеш таблицы это 30+ лет опыта самого языка работы с ними. Там всё уже продумано, для любой типовой ситуации. Но, хеш табля не вершина, всё зависит от данных, как они хранятся, сколько их, как их нужно обходить и реорганизовывать. Но да, хеш таблички это одно из самых важных изобретений в программировании.

Но стоит помнить что не всё так просто, изначально никаких таблиц нет, это после некого порога таблицы становятся хеш таблицами, ну и коллизии разрешаются уже связными списками и деревьями, без них никак, шеш табли это скорее в целом подход, в основе их реализации могут лежать и лежат разные алгоритмы и порой ещё применяются разные в зависимости от ситуации, тут так, там эдак. Коллизий меньше 10 можно хранить в массиве, больше 20 в листе, так как небольшие объёмы данных быстрее просто перебрать, чем что-то мудрить, больше 50 в дереве и так далее, организация памяти и всё это вот.

Короче, химичь =) Лучшие решения что получились оставляй, остальное сохраняй куда-то на потом, пригодится. Так и получается дело сделал и ничего не удалил, а просто сохранил на потом, и норм всё.

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

Проблема в том, что в вовке есть интернирование таблиц, там одинаковые строки - один объект. А в чистом луа вне вовки нету.

В итоге лог в вовке весил 12мб, а в луа от 40 до 500 мб в зависимости от реализации. Ну и как тут тестировать?

Вот сейчас мне надо протестировать в вовке всетаки префиксное дерево - ну то, зря я его пилил, блин?! Надо придумать применение. Затем протестировать хэш-таблицы. Сравнить.

Затем сравнить оба варианта со сжатым в них контеном и сравнить всех четверых…

А код я сохраняю. Ну реально хорошо вышло.

https://pastebin.com/0XU56T15

Ну вот ты глянь, глняь. Ну красота же. Все работает, добавляется, ищется. По словам, по частям. Каждому слову добавляется его номер.

А вот тут у меня работа с базами данных. Ну, шаблон измененный. Я сначала написал для работы с хэш-таблицами неизмененными, потом удалил все нахрен, переписал под работу со строками:

https://pastebin.com/YJ2rSwdH

Теперь надо переделать для работы с массивами - хэш-таблицы, но группами:

tbl = {
        {"строка1" = 1, "строка2" = 2, "строка3" = 3},
        {"строка4" = 4, "строка5" = 5, "строка6" = 6}
      }

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

Короче.. работать и работать.. И каждый день что то новое.

А еще зацени: https://pastebin.com/5E62UNpk

Я вот работаю с оптимизацией вот этих вот размеров, скорости и кааак же я хочу доступ к модулями си. Ты вообще понимаешь какие там возможности переделать все это? Этот бот на питоне со страта жрет 26мб, на си 4мб.

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

там одинаковые строки - один объект. А в чистом луа вне вовки нету.

В любой Lua, все строки уникальны и имеют один экземпляр, 10 одинаковых строк, это одна строка + 9 указателей не неё.

есть интернирование таблиц

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

В итоге лог в вовке весил 12мб, а в луа от 40 до 500 мб в зависимости от реализации.

Странно, что-то тут не то.

Ну и как тут тестировать?

Ну такое в рамках игры только, но в целом не понимаю, слишком большая разница. Но если тестировать отдельные куски кода вне игры, то только на наиболее близкой версии интерпретатора.

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

Я сейчас допишу 2-4 версии и запущу отдельными аддонами на месяц. В конце сравню их. Изи. Только зря я пытался все варианты работы с таблицами в один класс поместить. Нужно разные классы, разные методы.

Вообще как то я поздно пришел к идее разделения логики. Вот нахрена я пытался раз за разом учесть все в единой схеме? Где смысол? Мне что, места в файле жалко?

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

Слушай, а чего это? Как это вообще, я не понимать.

Мне тут дали ссылку на чатгпт китайский. Я с ним немного пообщался, дал задачу «создать такой то код». Он сделал. Гораздо удобнее, проще и продуманнее, чем я. Я конечно еще поуточнял, доработал часа 2. Но он сделал за 10 секунд (плюс 2 часа доработок) то, на что я убил до этого два дня.

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

Я бы вручную так не сделал.

Скормил ему свой код по кусочка. Он указал кучу оптимизаций, убрал повторы, добавил проверки, недописанную функциональность.

Я в недоумении.

Не, я шутил еще 4 года назад, что программисты вымрут, что это все автоматизируется, но…

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

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

Дал ему задачу с подвохом сделать графические крестики нолики для вовки. Он сделал за день. Явно тоже через чатгпт.

То есть нейросетка уже знает не только луа, но конкретно луа для вов? Или «нуб» её перед этим научил, подсказал, куда смотреть?

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

Она знает луа не просто для вовки, но даже для конкретной версии вовки. Не надо учить. Просто уточняешь версию и оно даст код нужный.

Вся задача - именно указать код чего тебе надо. То есть чуть чуть понимать задачу и структуру кода надо.

Оно знает различия между версиями языков, версиями игр. Между разными языками.

Например: "Создай класс для отрисовки 100 кнопок wow 3.3.5. Кнопки должны быть размером 64х64 пикселя, располагаться квадратом 10х10 в центре экрана. Привязаны должны быть к первой кнопке и перемещатья по экрану за нее. Их нельзя переместить за пределы экрана. Добавь чтобы при клике на кнопку выводился номер кнопки

Сделай так, чтобы при наведении на любую кнопку все кнопки уходили на предпоследний слой и перекрывали остальные элементы интерфейса. Если мышь убрать с кнопок, они должны уходить на самый низкий слой. "

Секунд 5-6 он писал код:

local ButtonGrid = {}
ButtonGrid.__index = ButtonGrid

-- Конструктор класса
function ButtonGrid:Create()
    local self = setmetatable({}, ButtonGrid)
    self.buttons = {} -- Таблица для хранения кнопок
    self.buttonSize = 64 -- Размер кнопки (64x64 пикселя)
    self.gridSize = 10 -- Размер сетки (10x10)
    self.spacing = 5 -- Расстояние между кнопками
    self:CreateButtons()
    return self
end

-- Создание кнопок
function ButtonGrid:CreateButtons()
    local totalSize = (self.buttonSize + self.spacing) * self.gridSize - self.spacing
    local startX = (GetScreenWidth() - totalSize) / 2
    local startY = (GetScreenHeight() - totalSize) / 2

    for i = 1, self.gridSize do
        for j = 1, self.gridSize do
            local buttonIndex = (i - 1) * self.gridSize + j -- Номер кнопки
            local button = CreateFrame("Button", nil, UIParent, "UIPanelButtonTemplate")
            button:SetSize(self.buttonSize, self.buttonSize)
            button:SetPoint("TOPLEFT", startX + (j - 1) * (self.buttonSize + self.spacing), -(startY + (i - 1) * (self.buttonSize + self.spacing)))
            button:SetText("") -- Убираем текст
            button:SetMovable(true)
            button:RegisterForDrag("LeftButton")

            -- Обработчик клика на кнопку
            button:SetScript("OnClick", function()
                print("Кнопка номер: " .. buttonIndex) -- Выводим номер кнопки в чат
            end)

            -- Обработчик наведения на кнопку
            button:SetScript("OnEnter", function()
                self:SetButtonsStrata("HIGH") -- Переводим все кнопки на предпоследний слой
            end)

            -- Обработчик ухода мыши с кнопки
            button:SetScript("OnLeave", function()
                self:SetButtonsStrata("BACKGROUND") -- Возвращаем все кнопки на самый низкий слой
            end)

            -- Привязка первой кнопки к перемещению
            if i == 1 and j == 1 then
                button:SetScript("OnDragStart", function() button:StartMoving() end)
                button:SetScript("OnDragStop", function()
                    button:StopMovingOrSizing()
                    self:UpdateButtonPositions(button)
                end)
            else
                -- Остальные кнопки привязываем к первой
                button:SetScript("OnDragStart", nil)
                button:SetScript("OnDragStop", nil)
            end

            table.insert(self.buttons, button)
        end
    end
end

-- Установка слоя для всех кнопок
function ButtonGrid:SetButtonsStrata(strata)
    for _, button in ipairs(self.buttons) do
        button:SetFrameStrata(strata)
    end
end

-- Обновление позиций кнопок относительно первой
function ButtonGrid:UpdateButtonPositions(mainButton)
    local mainX, mainY = mainButton:GetLeft(), mainButton:GetTop()
    local screenWidth, screenHeight = GetScreenWidth(), GetScreenHeight()

    -- Ограничение перемещения за пределы экрана
    local totalGridWidth = self.buttonSize * self.gridSize + self.spacing * (self.gridSize - 1)
    local totalGridHeight = totalGridWidth

    -- Ограничение по левому и правому краю
    mainX = math.max(0, math.min(mainX, screenWidth - totalGridWidth))
    -- Ограничение по верхнему и нижнему краю
    mainY = math.max(totalGridHeight, math.min(mainY, screenHeight))

    mainButton:ClearAllPoints()
    mainButton:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", mainX, mainY)

    for i = 1, #self.buttons do
        if self.buttons[i] ~= mainButton then
            local row = math.floor((i - 1) / self.gridSize) + 1
            local col = (i - 1) % self.gridSize + 1
            local offsetX = (col - 1) * (self.buttonSize + self.spacing)
            local offsetY = (row - 1) * (self.buttonSize + self.spacing)
            self.buttons[i]:ClearAllPoints()
            self.buttons[i]:SetPoint("TOPLEFT", mainButton, "TOPLEFT", offsetX, -offsetY)
        end
    end
end

-- Создаем экземпляр класса
local buttonGrid = ButtonGrid:Create()

Результат: https://rutube.ru/video/private/3b499f2ac90b71a81ad9d135050bc3ce/?p=w0Ls05sfm467-n4Iy4LHVQ

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

При этом он же все правильно сделал, собака. Учел размеры,расстояния. Главное - задай ему задачу. Я вот на эту срань помню потратил много суток. Чтобы подогнать под конкретный сервер, чтоб все нормально работало, чтобы все учесть. Чтобы разобраться как вообще это рисовать. Два года у меня ушло на понимание что такое вообще классы. А тут 5 секунд - держи.

Я перед этим неделю общался с ним на тему отличий человека и нейросети. Я его убеждал, что человек по сути та же нейросеть, но сложнее, он меня, что нейросеть говно, а человек вершина эволюции. Очень аргументированно убеждал, с отсылками на философов, науку, цитаты кидал и прочее. С человеками так конструктивно очень редко удается побеседовать В конце сошлись на том, что пока что нет технологий для создания нейросети уровня человека. Технологий и уровня понимания что такое человек.

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

https://chat.deepseek.com/

Там два уровня. Условно: студент и профессор. Если включить профессора, это 50 сообщений в сутки. Он более глубоко на английском все пытается анализировать. Студент - общий режим, он подружелюбнее и безлимитен. И общается на том языке, который тебе ближе.

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

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

Например нуб может попросить - нарисуй кнопку и оно нарисует. Ззеленую, красную, каскад кнопок. Потому что нуб знает что ему надо.

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

Это уменьшение рутины.

Но если ты не знаешь чего делать, оно за тебя не сделает и не найдет.

Нельзя просто попросить «сделай аддон убить всех». Нужно четко определить как ты хочешь всех убить. Через какие инструменты, доступны ли эти инструменты. Когда их вызывать и как.

Очень хороший инструмент, эффективность которого, как всегда, зависит от квалификации.

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

В принципе, я еще поэкспериментирую, понаблюдаю, но уже можно дать предварительный вердикт: хэш-таблицы сосут.

https://s.iimg.su/s/22/Oe6CSXfuGS7XFc1XzCzqX2EfmhqGtKTlHQZzU4bj.jpg

Вот я вывожу память четырех вариантов использования:

  1. лог в строках с шифрованием 37кб весь лог - 5000 сообщений https://paste.fo/raw/c0f322ac4016

37+281кб = 318кб. При этом большая часть словарь

  1. Словарь для шифрования - 280кб весь словарь. Почти не растет. https://paste.fo/raw/69fdcfabb5b5

  2. лог в строках БЕЗ шифрования. https://paste.fo/raw/6b489366bf4d

357кб

  1. лог в хэш-таблицах с шифрованием https://paste.fo/raw/58bae15ae727

502+281 = 782кб

  1. лог в хэш-таблицах без шифрования. https://paste.fo/raw/e9b97f1a8096

794кб

Соотношение и пропорции размеров практически без изменения со старта. Разве что словарь рос сначала быстро. Сейчас почти не растет.

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

Ух ты, шахматы?! Я как раз собиралсяих пилить. Но модификацию.

Перепилить твой аддон под мою версию (3.3.5) невозможно. Слишком много новых функций. А потом - увы.

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

Ошибочка. У меня из за ошибки в один из логов добавлялись не все слова. На самом деле со строками все как я описал, а хэштаблицы идентичны практически получаются. Хоть шифрованные, хоть нет.

В 2-8 раз больше строк. И это со старта. Точнее разнциа между шифрованной и не шифрованной нарастает постепенно. Со старта почти идентичны, на мегабайте разница процентов 20 уже наверное.

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