How can I improve the performance of this procedural map visualization script?

Hello, I’d like to get tips on how to improve my model-pasting-welding code of the terrain generation script.
(it’s part of another function)

        local assets = game.ReplicatedStorage.Assets.MapParts
        local LandBlocks = {assets.StoneBlock, assets.CorruptedStoneBlock}
		local DeepBlocks = {assets.BasaltBlock, assets.CorruptedBasaltBlock}
		local GrassBlocks = {assets.Grass, assets.CorruptedGrass}
		local C_HEIGHT = wp.MainIslands.Height
		
		local function getPosCons(pos:Vector3): {Model} --there are only 3 positions that can already be in the map folder (4 if part pos is x, 1, z)
			local pos0, pos1, pos2 = pos-Vector3.new(1, 0, 0), pos-Vector3.new(0, 0, 1), pos+Vector3.new(0, 1, 0)
			local cons = {}
			local f0, f1, f2 = game.Workspace.Map:FindFirstChild(tostring(pos0)), game.Workspace.Map:FindFirstChild(tostring(pos1)), game.Workspace.Map:FindFirstChild(tostring(pos2))
			if f0 then table.insert(cons, f0) end
			if f1 then table.insert(cons, f1) end
			if f2 then table.insert(cons, f2) end
			if pos.Y == 1 then
				local pos3 = pos-Vector3.new(0, 1, 0)
				local f3 = game.Workspace.Map:FindFirstChild(tostring(pos3))
				if f3 then table.insert(cons, f3) end
			end
			return cons
		end
		
		local function createWeld(parent:Instance, primary:BasePart, primary2:BasePart)
			local weld = Instance.new("WeldConstraint")
			weld.Part0 = primary
			weld.Part1 = primary2
			weld.Name = "MapWeld"
			weld.Parent = parent
		end
		
		local function pasteModel(pos:Vector3, model:Model, name:string?, rot:CFrame?)
			local clone = model:Clone()
			clone.Parent = game.Workspace.Map
			rot = rot or CFrame.Angles(0, math.rad(math.round(RANDOM:NextInteger(0, 400)/100)*90), 0)
			clone:PivotTo(CFrame.new(pos)*rot)
			local pos2 = pos/10
			pos2 = Vector3.new(math.round(pos2.X), math.round(pos2.Y), math.round(pos2.Z))
			clone.Name = name or tostring(pos2)
			if clone.PrimaryPart then clone.PrimaryPart.Anchored = true else clone:FindFirstChildWhichIsA("BasePart", true).Anchored = true end
			return clone
		end
		
		local function afterPaste(model:Model)
			task.wait(0.5)
			local primary = model.PrimaryPart or model:FindFirstChildWhichIsA("BasePart", true)
			local pos2 = model:GetPivot().Position/10
			pos2 = Vector3.new(math.round(pos2.X), math.round(pos2.Y), math.round(pos2.Z))
			if model.Name:find("Grass") then
				local m = game.Workspace.Map:FindFirstChild(tostring(pos2))
				if not m then return end
				createWeld(model, primary, m.PrimaryPart or m:FindFirstChildWhichIsA("BasePart", true))
			else
				for _, m:Model in pairs(getPosCons(pos2)) do
					local primary2 = m.PrimaryPart or m:FindFirstChildWhichIsA("BasePart", true)
					createWeld(model, primary, primary2)
				end
			end
		end

		local function prepRandIndex(index:number, max:number)
			local index2 = math.round(index*max)
			return index2 == 0 and 1 or index2
		end
		
		local function groupAfterPaste(models: {Model})
			for _, m:Model in pairs(models) do
				coroutine.wrap(afterPaste)(m)
			end
		end
		
		for i, pos2:Vector3 in pairs(worldBlocks) do --worldBlocks is a table with positions (Vector3)
			if game.Workspace.Map:FindFirstChild(tostring(pos2)) then continue end
			local pos = pos2*10
			local zeroPos = Vector3.new(pos2.X, 0, pos2.Z)
			local pastedModels = {} :: {Model}
			local randomIndex = RNG(0, 100)/100
			local randomInteger = RNG(0, 100)/100
			--try to find a part at 0, 0, 0 and if not then create it
			if table.find(worldBlocks, zeroPos) and pos2.Y > 0 and pos2.Y < C_HEIGHT+1 and (Vector3.new(pos2.X, 0, pos2.Z).Magnitude <= wp.MainIslands.CircleRadius+RMX+1) then
				local zeroBlock = game.Workspace.Map:FindFirstChild(tostring(zeroPos)) :: Model?
				if not zeroBlock then
					if randomInteger*3 > 2 then
						coroutine.wrap(afterPaste)(pasteModel(zeroPos*10, assets.LadderAbandonedBlock))
						coroutine.wrap(afterPaste)(pasteModel(pos, assets.LadderAbandonedBlock))
						continue
					else
						table.insert(pastedModels, pasteModel(zeroPos*10, DeepBlocks[prepRandIndex(randomIndex, #DeepBlocks)]))
					end
				elseif table.find(zeroBlock:GetTags(), "Ladder") then
					coroutine.wrap(afterPaste)(pasteModel(pos, assets.LadderAbandonedBlock, nil, zeroBlock:GetPivot().Rotation))
					continue
				end
			end
			
			if pos2.Y == C_HEIGHT then
				local index = prepRandIndex(randomIndex, #LandBlocks)
				table.insert(pastedModels, pasteModel(pos, LandBlocks[index]))
				if not table.find(worldBlocks, pos2+Vector3.new(0, 1, 0)) and randomInteger < 0.5 then
					table.insert(pastedModels, pasteModel(pos, GrassBlocks[index], "Grass "..tostring(pos2)))
				end
			elseif pos2.Y < C_HEIGHT then
				table.insert(pastedModels, pasteModel(pos, DeepBlocks[prepRandIndex(randomIndex, #DeepBlocks)]))
			end
			
			--now weld it up!
			coroutine.wrap(groupAfterPaste)(pastedModels)
			
			
			if i % 100 == 0 then
				task.wait()
				print("Pasting "..i.."/"..#worldBlocks)
			end
		end

Everything works fine, but I need more performance. On my pc it takes only 2-4 mins to generate a 300 * 300 blocks world with 100 blocks huge islands circle radius. But on the roblox server it’s 6-8 mins. I want it to take only up to 3 mins.
I already tried checking less positions for connections (cons), getting random number only 2 times in a loop and the coroutines. It worked, but I still need more performance and Idk what to do.