Why is game.Players.NumPlayers dilapidated?

Why is this property dilapidated? By which I mean, Roblox discourages its use for new work. The alternative is supposedly #game.Players:GetPlayers() but which do you think is faster…

  • Reading a simple property?

Or…

  • Creating a table, populating it with the players, just to count the number of elements and leave it for the garbage collector?

Well obviously the first one should be much faster. So why is NumPlayers dilapidated?

I remember seeing a post suggesting to dilapidate this property before it actually became dilapidated, and guess what? I wholeheartedly disagree.

It’s not really a huge deal.

#game.Players:GetPlayer() has a time complexity of O(n) which compared to NumPlayers, a simple look-up with the time complexity of O(1), is slower.
However, the difference between O(1) and O(n) is only noticable when used on bigger sets of data.
But since the ROBLOX Servers are on average not even close to 40 or 60 active players its making the difference in performance immeasurable.

There are two reasons that I can think of to why they discourage the use of NumPlayers:

  1. Like I already said, it’s not a huge deal and they plan to remove it in the future.
  2. It’s something that made it into the Studio-Engine on accident and they haven’t removed it since.

Either way, I’d use #game.Players:GetPlayers() as it makes no measurable difference and in addition, I know that this is something that they will not touch/ remove in the future, making it future-safe code.

Here is a test I ran in studio to show how little the difference is (run on 100 simulated servers with random players from 1 to 100 using 10000 runs):

local results_linea = {}
local results_const = {}

for i = 1, 100 do
	local randomNum = math.random(1, 100) -- testing on various servers with a random playercount from 1 to 100

	local constLookUp = {NumPlayers=randomNum}

	local lineaLookUp = {}

	-- populating the other table with unique data
	for i = 1, randomNum do
		table.insert(lineaLookUp, {Name="Player"..math.random(0,10000)})
	end


	do  -- time on constant look-up
		for i = 1, 10000 do
			local start = os.clock()
			
			local players = constLookUp.NumPlayers
			
			table.insert(results_const, os.clock()-start)
		end
	end


	do	-- time on linea look-up 
		for i = 1, 10000 do
			local start = os.clock()

			local players = #lineaLookUp

			table.insert(results_linea, os.clock()-start)
		end
	end
end


local function getBest(tab)
	local best = tab[#tab]
	
	for i, v in pairs(tab) do
		if (v < best) then
			best = v
		end
	end
	
	return best
end

local function getWorst(tab)
	local worst = tab[#tab]
	
	for i, v in pairs(tab) do
		if (v > worst) then
			worst = v
		end
	end
	
	return worst
end

local function getAVG(tab)
	local avg = 0
	
	for i, v in pairs(tab) do
		avg += v
	end
	
	return avg / #tab	
end

print("Results:")
print("LINEA-BEST: "..getBest(results_linea).." | LINEA-WORST: "..getWorst(results_linea).." | LINEA-AVG: "..getAVG(results_linea))
print("CONST-BEST: "..getBest(results_const).." | CONST-WORST: "..getWorst(results_const).." | CONST-AVG: "..getAVG(results_const))

(not the best code but I am really tired rn)

results:

As we can see, constant look-up is faster, but only 0.000000001448 Seconds on average.

Here is the result if there were to be 1000 to 3000 players on a single server at the same time:

The impact is still to little to notice: 0.000000002588 seconds on average.
This is 0.00000000114 seconds slower than the prior test.

1 Like

Where did it way it made it in by accident?

That’s a question only ROBLOX themselves can answer.

My best guess is that NumPlayers was a thing to make code more readable/ better to understand for beginners, as the syntax is just cleaner compared to #game.Players:GetPlayers(), however they seemed to have noticed that this may not be as big of a deal as one might think.