Performance Tips on Grid Creation

Hey, welcome to my post. Its in regards to creating very large grid data representation.

When a player initally connects to the game, the server has to boot up. I have this script on server bootup that basically creates Grid[x][y]

Can someone provide some performance tips to this ?

for x=1,gridSizeX, 1 do
		Grid[x] = {}
		for y=1,gridSizeY, 1 do
			local WorldPoint = worldBottomLeft + Vector3.new(1,0,0) * (x * nodeDiameter + nodeRadius) + Vector3.new(0,0,1) * (y * nodeDiameter + nodeRadius)
			local min = WorldPoint - (((nodeDiameter + nodeRadius)) * part.Size)
			local max = WorldPoint + (((nodeDiameter + nodeRadius)) * part.Size)
			local currentRegion = Region3.new(min, max)
			local notWalkableFound = false
			for _,Part in pairs(game.Workspace:FindPartsInRegion3(currentRegion,nil,5)) do
  				if(Part.Parent.Name == "Unwalkable" == true) then
					notWalkableFound = true
					break;
				end
			end
			
			local walkable = true
			if(notWalkableFound == true) then
				walkable = false
			end
			local penalty = 1
			if(walkable == false) then
				penalty = penalty + obstaclePromityPenalty
			end
			print("GridStorage["..x.."]["..y.."] = NodeClass.new(", walkable, ", Vector3.new(", WorldPoint.X, ", 0, ", WorldPoint.Z, "), ",x,", ",y,", ",penalty,")")
			--Grid[x][y] = NodeClass.new(walkable, Vector3.new(WorldPoint.X, 0, WorldPoint.Z), x, y, penalty)--]]
		end
		wait()
	end

This takes pretty long to load up so I was wondering if someone could help optimize the code here a bit. Enough to make a dent in the time it takes to load all of them

1 Like

Avoid doing the same calculations repeatedly – I can see lots of things related to nodes that could be calculated once then stored in a variable, or calculated for every x value (not for every y in every x).

Example:

local nodeDimensions = nodeDiameter + nodeRadius -- Just made up a random name for this one
local nodeSize = nodeDimensions * part.Size

for x = 1, gridSizeX do
    local nodeX = x * nodeDimensions

    for y = 1, gridSize Y do
        --  code here
    end
end

Note you can use workspace instead of game.Workspace (probably won’t have a noticeable impact on performance, but it helps for writing).

The not walkable detection code will probably be a big slowdown too. If it’s not instantly needed, consider making that all run after creating the grid.

Also you have a lot of redundant code that will be impacting performance (even if its minor). For example:
Part.Parent.Name == "Unwalkable" == true in which that last check for == true is pointless.
for x = 1, gridSize, 1 do that last 1 is redundant because the for loop increments by 1 by default.

Also: not sure exactly how you know the performance issues are from your code, as note that Roblox servers can take a while to start up anyway.

Anyway, hopefully this helps :wink:

You’re yielding after every iteration of x, which is going to slow down your code. What is the intention for this?

The minimum yield in Roblox Lua is roughly 1/30 seconds, which will be the yield time if no argument is passed to wait

2 Likes

I’m fairly certain this is going to have a huge impact. Constantly creating new string objects, as well as printing them to the console, takes a long time. I tried doing a benchmark of three different versions of the same terrain generation function:

  1. No string objects and no print statements
  2. String objects but no print statements
  3. String objects printed to the console

I should probably have two additional versions, where I only create and print strings for every row, instead for every row and column. But oh well.

I don’t really know anything about how to do proper benchmarks, but I feel like my results were pretty significant even if there’s a lot of noise and/or systematic error.

Here are my benchmark results

Terrain with no string and no print:
Iterations: 100
Time: 12.670662
Terrain with string and no print:
Iterations: 100
Time: 12.525537
Terrain with string and print:
Iterations: 100
Time: 66.672688

Here's my benchmark code

local noise = math.noise
local freq = 8
local power = 1
local size = 64
local seed = os.time()

local p = Instance.new(“Part”)
p.Size = Vector3.new(1, 1, 1)
p.Anchored = true

function generateTerrain1()
local terrain = Instance.new(“Model”)

for x = 1, size do
  for y = 1, size do
    local v = noise((freq*x)/(size), (freq*y)/(size), seed) * power
    local p = p:Clone()
    p.CFrame = CFrame.new(x, v, y)
    p.Parent = terrain
  end
end
  
terrain.Parent = game.Workspace
return terrain

end

function generateTerrain2()
local terrain = Instance.new(“Model”)

for x = 1, size do
  for y = 1, size do
    local v = noise((freq*x)/(size), (freq*y)/(size), seed) * power
    local p = p:Clone()
    p.CFrame = CFrame.new(x, v, y)
    p.Parent = terrain
    local s = string.format("%d, %d", x, y)
  end
end
  
terrain.Parent = game.Workspace
return terrain

end

function generateTerrain3()
local terrain = Instance.new(“Model”)

for x = 1, size do
  for y = 1, size do
    local v = noise((freq*x)/(size), (freq*y)/(size), seed) * power
    local p = p:Clone()
    p.CFrame = CFrame.new(x, v, y)
    p.Parent = terrain
    local s = string.format("%d, %d", x, y)
    print(s)
  end
end
  
terrain.Parent = game.Workspace
return terrain

end

function benchmark(f, n, title)
local tick = tick
local t0 = tick()

for i = 1, n do
  local t1 = tick()
  wait()
  local t = tick()
  t0 = t0 + (t - t1)
  
  f()
end

local t = tick()

return string.format("%s:\n\tIterations: %d\n\tTime: %f", title, n, (t-t0))

end

local message = {}
table.insert(message, benchmark(function() generateTerrain1():Destroy() end, 100, “Terrain with no string and no print”))
table.insert(message, benchmark(function() generateTerrain2():Destroy() end, 100, “Terrain with string and no print”))
table.insert(message, benchmark(function() generateTerrain3():Destroy() end, 100, “Terrain with string and print”))

print(table.concat(message, “\n”))

You should probably take the exact results with a huge grain of salt, but I think it’s safe to say that printing all the time is a bad idea. This is in line with Lua Performance Tips from lua.org.

1 Like