majqq

bone_attach - performance

Recommended Posts

Hey. I would like to integrate bone attach with my gamemode, and also improve code a bit.

First of all, question about meta of bone attach.

<script src="bone_attach.lua" />
<script src="bone_attach_c.lua" type="client" cache="false" />
<script src="attach_func.lua" />
<script src="attach_func.lua" type="client" cache="false" />
<script src="bone_pos_rot.lua" type="client" cache="false" />

If script doesn't have defined type, then by default it's server/shared?

So i thought about those ways.

1. Use more locals - following this performance tips?

2. Replace onClientRender to render events by IIYAMA - probably this should help a little bit though.

3. Any other way which i don't know about?

 

Share this post


Link to post
8 hours ago, majqq said:

If script doesn't have defined type, then by default it's server/shared?

server by default.

 

 

8 hours ago, majqq said:

3. Any other way which i don't know about?

A critical one:

Loop over an array table structure. Not an object table structure.

But that doesn't mean you can't use both.

 

local boneAttachments_array = {}
local boneAttachments_object = {}

 

local newAttachment = {
	posX,
	posY,
  	ped -- etc.
}


boneAttachments_array[#boneAttachments_array + 1] = newAttachment
boneAttachments_object[ped] = newAttachment

 

Access the (same) data:

for i=1, #boneAttachments_array do
	local boneAttachment = boneAttachments_array[i]
end

 

local boneAttachment = boneAttachments_object[ped]

 

 

  • Like 1

Share this post


Link to post
6 hours ago, IIYAMA said:

server by default.

 

 

A critical one:

Loop over an array table structure. Not an object table structure.

But that doesn't mean you can't use both.

 


local boneAttachments_array = {}
local boneAttachments_object = {}

 


local newAttachment = {
	posX,
	posY,
  	ped -- etc.
}


boneAttachments_array[#boneAttachments_array + 1] = newAttachment
boneAttachments_object[ped] = newAttachment

 

Access the (same) data:


for i=1, #boneAttachments_array do
	local boneAttachment = boneAttachments_array[i]
end

 


local boneAttachment = boneAttachments_object[ped]

 

 

Where exactly i should use int loop, or we're about changing it in this function?

function forgetNonExistingPeds()
	local checkedcount = 0
	while true do
		for element,ped in pairs(attached_ped) do
			if not isElement(ped) then clearAttachmentData(element) end
			checkedcount = checkedcount+1
			if checkedcount >= 1000 then
				coroutine.yield()
				checkedcount = 0
			end
		end
		coroutine.yield()
	end
end

 

Share this post


Link to post
1 hour ago, majqq said:

Where exactly i should use int loop, or we're about changing it in this function?


function forgetNonExistingPeds()
	local checkedcount = 0
	while true do
		for element,ped in pairs(attached_ped) do
			if not isElement(ped) then clearAttachmentData(element) end
			checkedcount = checkedcount+1
			if checkedcount >= 1000 then
				coroutine.yield()
				checkedcount = 0
			end
		end
		coroutine.yield()
	end
end

 

 

At places with this kind of loops:

for element,ped in pairs(attached_ped) do

 

If you want to optimise this, it will take at least an hour to implement the new table structure. Ask yourself the question: "Do you want to spend your time on that?"

 

Also the code from your previous reply could be merged with:

Spoiler

function putAttachedElementsOnBones()
	for element,ped in pairs(attached_ped) do
		if not isElement(element) then
			clearAttachmentData(element)
		elseif isElementStreamedIn(ped) then
			local bone = attached_bone[element]
			local x,y,z = getPedBonePosition(ped,bone_0[bone])
			local xx,xy,xz,yx,yy,yz,zx,zy,zz = getBoneMatrix(ped,bone)
			local offx,offy,offz = attached_x[element],attached_y[element],attached_z[element]
			local offrx,offry,offrz = attached_rx[element],attached_ry[element],attached_rz[element]
			local objx = x+offx*xx+offy*yx+offz*zx
			local objy = y+offx*xy+offy*yy+offz*zy
			local objz = z+offx*xz+offy*yz+offz*zz
			local rxx,rxy,rxz,ryx,ryy,ryz,rzx,rzy,rzz = getMatrixFromEulerAngles(offrx,offry,offrz)

			local txx = rxx*xx+rxy*yx+rxz*zx
			local txy = rxx*xy+rxy*yy+rxz*zy
			local txz = rxx*xz+rxy*yz+rxz*zz
			local tyx = ryx*xx+ryy*yx+ryz*zx
			local tyy = ryx*xy+ryy*yy+ryz*zy
			local tyz = ryx*xz+ryy*yz+ryz*zz
			local tzx = rzx*xx+rzy*yx+rzz*zx
			local tzy = rzx*xy+rzy*yy+rzz*zy
			local tzz = rzx*xz+rzy*yz+rzz*zz
			offrx,offry,offrz = getEulerAnglesFromMatrix(txx,txy,txz,tyx,tyy,tyz,tzx,tzy,tzz)
			if (string.find(objx..objy..objz, "a")) then
			  setElementPosition(element, x, y, z)
			else
			  setElementPosition(element, objx, objy, objz)
			end
			if (not string.find(offrx..offry..offrz, "a")) then
			  setElementRotation(element, offrx, offry, offrz, "ZXY")
		else
			setElementPosition(element,getElementPosition(ped))
			end
		end
	end
end
addEventHandler("onClientPreRender",root,putAttachedElementsOnBones)

 

 

 

 

  • Like 1

Share this post


Link to post
1 hour ago, IIYAMA said:

 

At places with this kind of loops:


for element,ped in pairs(attached_ped) do

 

If you want to optimise this, it will take at least an hour to implement the new table structure. Ask yourself the question: "Do you want to spend your time on that?"

 

Also the code from your previous reply could be merged with:

  Hide contents


 
function putAttachedElementsOnBones()
	for element,ped in pairs(attached_ped) do
		if not isElement(element) then
			clearAttachmentData(element)
		elseif isElementStreamedIn(ped) then
			local bone = attached_bone[element]
			local x,y,z = getPedBonePosition(ped,bone_0[bone])
			local xx,xy,xz,yx,yy,yz,zx,zy,zz = getBoneMatrix(ped,bone)
			local offx,offy,offz = attached_x[element],attached_y[element],attached_z[element]
			local offrx,offry,offrz = attached_rx[element],attached_ry[element],attached_rz[element]
			local objx = x+offx*xx+offy*yx+offz*zx
			local objy = y+offx*xy+offy*yy+offz*zy
			local objz = z+offx*xz+offy*yz+offz*zz
			local rxx,rxy,rxz,ryx,ryy,ryz,rzx,rzy,rzz = getMatrixFromEulerAngles(offrx,offry,offrz)

			local txx = rxx*xx+rxy*yx+rxz*zx
			local txy = rxx*xy+rxy*yy+rxz*zy
			local txz = rxx*xz+rxy*yz+rxz*zz
			local tyx = ryx*xx+ryy*yx+ryz*zx
			local tyy = ryx*xy+ryy*yy+ryz*zy
			local tyz = ryx*xz+ryy*yz+ryz*zz
			local tzx = rzx*xx+rzy*yx+rzz*zx
			local tzy = rzx*xy+rzy*yy+rzz*zy
			local tzz = rzx*xz+rzy*yz+rzz*zz
			offrx,offry,offrz = getEulerAnglesFromMatrix(txx,txy,txz,tyx,tyy,tyz,tzx,tzy,tzz)
			if (string.find(objx..objy..objz, "a")) then
			  setElementPosition(element, x, y, z)
			else
			  setElementPosition(element, objx, objy, objz)
			end
			if (not string.find(offrx..offry..offrz, "a")) then
			  setElementRotation(element, offrx, offry, offrz, "ZXY")
		else
			setElementPosition(element,getElementPosition(ped))
			end
		end
	end
end
addEventHandler("onClientPreRender",root,putAttachedElementsOnBones)

 

 

 

 

I would try, i'm sure by following your advice i could do that :P

I just need a point to start off, and some more time (besides that, i'm doing a lot of other things, i'm asking about that, because i will need it in nearest future.)

Share this post


Link to post

Hey @IIYAMA

Each day, with small steps am getting closer to that.

But before i am going to start optimizing bone_attach, i want to be sure that i've optimized my other scripts correctly.

This refers to Lua functions and MTA functions which are in _G table?

So for example math. functions, tostring, tonumber, and shared functions which are available in client and server side aswell (getElementsByType, getElementPosition) ?

I'm just asking because after testing localized things it gives me a different result (less or more ticks)

Share this post


Link to post

 

28 minutes ago, majqq said:

This refers to Lua functions and MTA functions which are in _G table?

The _G table keeps track of all global variables. Not just functions and not all functions, as some can be saved in local variables.

https://www.Lua.org/pil/14.html

 

 

31 minutes ago, majqq said:

So for example math. functions, tostring, tonumber, and shared functions which are available in client and server side aswell (getElementsByType, getElementPosition) ?

Yes, they are `shared` or rather copied.

 

I honestly only localize my own functions. The last time I checked, I didn't notice any optimization by localize MTA functions. Every file has it's own scope, so making use of that is not a bad thing. My main reason for localization: You can use it to distinguish functions used within the file, from functions that are called from other files.

 

The only thing I do with some Lua functions is this:

local mathRandom = math.random

 

  • Like 1

Share this post


Link to post
5 hours ago, IIYAMA said:

 

The _G table keeps track of all global variables. Not just functions and not all functions, as some can be saved in local variables.

https://www.Lua.org/pil/14.html

 

 

Yes, they are `shared` or rather copied.

 

I honestly only localize my own functions. The last time I checked, I didn't notice any optimization by localize MTA functions. Every file has it's own scope, so making use of that is not a bad thing. My main reason for localization: You can use it to distinguish functions used within the file, from functions that are called from other files.

 

The only thing I do with some Lua functions is this:


local mathRandom = math.random

 

Hi, thanks for answer. I've checked by myself and probably it worked for Lua functions (looks like), but for some reason on MTA functions it showed mostly less or rarely - more ticks, there was a few times when it taked even more than non-localized things. (maybe fault of fast restart of script but i don't think so).

  • Like 1

Share this post


Link to post
11 hours ago, majqq said:

but for some reason on MTA functions it showed mostly less or rarely - more ticks, there was a few times when it taked even more than non-localized things.

I had the same results, so nothing has changed. It might even be possible that they are already localized in some unknown way. Just as the predefined source variable is localized when an event has been triggered.

  • Like 1

Share this post


Link to post
On 19/08/2019 at 17:28, IIYAMA said:

I had the same results, so nothing has changed. It might even be possible that they are already localized in some unknown way. Just as the predefined source variable is localized when an event has been triggered.

Hey @IIYAMA

Probably i've finished this.

I just need to fix rotation, and test server-side.

But at the moment, i've tested it only by myself, with 1 attached object.

This is the result:

sHPNbQG.png

- Localized Lua functions, tables, and any other functions which returns data.

- Used int loop instead of pairs, didn't took me so long since i'm practising tables from the time when you showed me how to sync data between server/client.

- With thing above i get rid of timer, coroutine and while loop. (hopefully this will fix an annoying error, i check if element actually exists, if not, script removing it from table)

Glad that you helped me ;)

Edited by majqq

Share this post


Link to post
2 hours ago, majqq said:

This is the result:

And what was it before? If you want to measure this correctly you need multiple samples. 0 AttachElements (idle). 10 AttachElements (doing something). 1000 AttachElements (doing a lot). 10000 AttachElements (doing too much).

Share this post


Link to post
2 hours ago, IIYAMA said:

And what was it before? If you want to measure this correctly you need multiple samples. 0 AttachElements (idle). 10 AttachElements (doing something). 1000 AttachElements (doing a lot). 10000 AttachElements (doing too much).

Sorry but i didn't last 60 seconds at last test, at 1000 objects :P

Screenshot comparing old one and new one, running at same time.

1 object:

k7aDxNx.png

10 objects:

mG1uL8K.png

100 objects:

ANSBzll.png

1000 objects:

rflF5Sy.png

  • Like 1

Share this post


Link to post
1 hour ago, majqq said:

Screenshot comparing old one and new one, running at same time.

More than 50% performance boost. 😎

Nicely done!

 

  • Like 1

Share this post


Link to post

So you say its always a bad idea to access a table with an element as index?
table[vehicle] is worse than table[number] ?
Is there any reasonable explanation why that is the case or is it just a funfact?

Edited by Einheit-101

Share this post


Link to post

@Einheit-101

If we keep looping out of it and only talk about indexing.

 

This below is just an assumption:

If this is the userdata key: (which is at the very end just a complex and unique identifier.)

"userdata-324ftfdbgfd"

 

And this is the data:

table = {
	["userdata-424ftfsdgsf"] = true,
	["userdata-3sd3524ftfd"] = true,
	["userdata-325524ftfdb"] = true,
	["userdata-324ftfdbgfd"] = true
}

 

Depending on which algorithm is used, the search time variates. (algorithm < which I have no knowledge of)

But if I would program this search, I might do it like this in my first try:

I know already the type, so my start would be: "userdata-"

>>>

userdata-424ftfsdgsf
userdata-3sd3524ftfd
userdata-325524ftfdb
userdata-324ftfdbgfd

Collect all items with userdata. steps * items

>>>

userdata-424ftfsdgsf
userdata-3sd3524ftfd
userdata-325524ftfdb
userdata-324ftfdbgfd

print(string.byte("3")) -- 51

+ update position * 4 + 4 steps

>>>


userdata-3sd3524ftfd
userdata-325524ftfdb
userdata-324ftfdbgfd

print(string.byte("s")) -- 115
print(string.byte("2")) -- 50

+ update position * 3 + 3 steps

>>>

print(string.byte("4")) -- 52


userdata-325524ftfdb
userdata-324ftfdbgfd < found item in table

+ update position *  2 + 2 steps

 

Lua would probably be better in this then I do. But this has multiple search steps in it, no matter what kind of algorithm you use.

 


 

table = {
	true, -- 1
	true, -- 2
	true, -- 3
	true -- 4
}

table = {
	[1] = true,
	[2] = true,
	[3] = true,
	[4] = true
}

 

Finding the index in this kind of table should be technically be faster based on two ways.

  1. Position of the characters do not matter. It is a single value, the cpu knows very well how to work with that.
    As with user-data's/strings, not only the position matters, the value is also different:
    print(string.byte("a")) -- 97

    Much complexer values X position.

  2. Everything is already placed in order if you keep the table as an clean array.
    Just as with cleaning your room. After the cleaning, you do not have to look everywhere in your room. You more or less know where to find what and how many things you have of it.

 

Searching for item 4?

no, no, no, yes

(4 steps)

 


 

Note: looping to find an item VS using a custom key to find an item is a different subject.

 


 

If somebody knows how this really works, then that would be nice fun facts.

 

 

 

 

 

 

 

Edited by IIYAMA
  • Thanks 1

Share this post


Link to post

So will you share with us your update?)

P.S. Thx for the link to Lua performance. Very Useful :)

Share this post


Link to post

So the central message of you is that a table is being looked up faster when the key is shorter, which makes sense. 

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.