List of Part Property Costs, by Frame Time

This is the cost of changing the property, not of the cast shadow itself.

4 Likes

Suprises me to see CFrame being more expensive than setting Position and Orientation separately.

1 Like

This is from 2017, but is it still applicable and if so don’t these results contradict what this is saying: Event that fires when rendering finishes - #19 by zeuxcg

Only very very barely. Setting both of those properties in sequence is about 2x as expensive as setting CFrame.

2 Likes

CFrame recalculates geometry, so it should make sense that it’s the most expensive property. Even before this data, myself and other developers have known for a very long time that CFrame is to be avoided when possible.

Also, that post doesn’t debate it since it’s not relative to other properties.

tl;dr you can’t argue data.

Most likely this has been implemented by now:

If in doubt, use BulkMoveTo.

Like you said aren’t these measurements calculating the cost of changing the property and not how it affects the environment? Shown below.

1 Like

Correct… and it’s in the title of the post.

I realize that. Here is the post for reference:

Your results show CFrame taking the most time, but that contradicts what the post in the image is saying.

You also stated this in response to my first reply, but saying this again contradicts what you said earlier to someone else:

The above quote would be the same thing as saying “this is the cost or changing the CFrame property not the recalculation of the geometry itself.”

So I’m assuming these results aren’t purely the speed of just changing the property? Can you explain the difference between what zeuxcg is saying and what your results are measuring?

Geometry calculation happens immediately when modifying the property. A CFrame modification is the geometry recalculation.

CastShadow is a boolean that affects whether the separate render thread will show a shadow, making it not immediate.

Again, you cannot argue hard data, so I’m not really sure what you’re getting at here. There really is nothing to disagree with, especially since you can test it yourself with the benchmark file I provided.

I think the main Lua thread is currently intertwined with the render thread, not separate. Parallel Luau will allow for Lua states that aren’t on the main thread.

Have they? I’ve never avoided using CFrame because of a 21 microsecond duration.

Actually… You can. There could be a number of issues going on here. Perhaps your PC is slower than other computers at intervals, or maybe setting the parent ten times repeatedly is much slower than setting it just once. The properties you’ve set them to don’t even equal each other (you’ve set reflectance to 1 but transparency to 0.5.)

No offense, but this is the most pointless micro-optimisation I’ve seen so far on the dev forum.

3 Likes

This is a really crappy way of measuring the performance impact of changing a parts property. There are wayyyy to many variables that can change the outcome that the guy above me said:

Also you can argue your “hard” data since it isn’t even reliable.

At the end of the day don’t change anything you were doing because of the “resource” because it is more useless than a white crayon.

1 Like

Microseconds does not mean micro-optimization, since repeating these operations repeatedly in one frame will drop framerate consistently and notably. These aren’t of use to you if you’re intentions are to work on simpler games that don’t use per-frame calculations.

Some crucial examples in my experience:

  • Custom animation handler; I had to optimize CFrame calculations per-frame to prevent lag issues with additive animation
  • Hitboxes; it was recommend to me by loleris a while back that custom hitboxes should not have a CFrame operation per-hitbox-part, but rather, it should be a simulated rig with only one CFrame operation per frame
  • Procedural map animation; Using a combination of depth-based truncation and MeshPart application, I saved a very large number of frames on a map that has more moving parts than static.

Expanding upon the CFrame operation, do realize that changing that property recalculates geometry; this forces the engine to update both the render and the collision cells. CFrame is more expensive than just doing position or orientation (as one or the other) since it’s a 4x4 matrix calculation.

Here are some demo places changing a part’s property a very high number of times per frame. The first one uses CFrame, other uses Transparency. No other differences.

CFrameDemo.rbxl (34.5 KB)
TransparencyDemo.rbxl (34.4 KB)

A micro-optimization would be something silly like replacing pairs with ipairs, since while it “saves” something, it doesn’t actually do anything. ( cc: @mostbootifulgirlever )

As a side note, no, my computer is fine.

If you don’t take my word for it, I can call over @PoptartNoahh to back me up here, since he has more experience with experimenting on the platform.

2 Likes

if you optimize your code by a few micro seconds that is a micro-optimization.

Literally basic optimization practices which don’t change how useless this “resource” is.

What do you even mean by “recalculates geometry”?

Clearly you have no goddamn idea on what the hell you are saying. At this point your just saying words to sound smart. What is a render and collision cell?

Also I’m sure a 4x4 matrix calculation should be easy enough for any pc’s CPU to calculate multiple times per frame without any lag.

ok?

He doesn’t need to back you up since everything your saying is wrong.


You make a lot of good resources and I use them all the time but this one’s a miss.

2 Likes

image

That’s his response, but anyways,

A render/collision cell is just that – a portion that is being rendered or collision-calculated. These are the parts of the game that connect to make, y’know, the whole thing.

Not if it’s many times per frame.

When your frame rate is lower by, let’s say, 10 FPS, that means your frame times are increased by a few milliseconds per-frame.

Again, you may check out my demos:

If you find no use of this resource, that’s your problem. However, it’s useful to developers such as myself that make many calculations per-frame.

It’s not single micro-second saved, it’s the thousands saved. It’s the same reason somebody should cull instead of destroy many times per frame.


Side note: A common definition of a micro-optimization is one that saves less than one microsecond of frame time, since it makes no difference. Typically, 10-20 is when it makes a difference when done many times per frame.

I’m not sure how your benchmarking plugin works, but wouldn’t this cache the result of the matrix multiplication result so when you iteratively set it there is no matrix multiplication actually happening.

So what you are measuring is just the “render and collision cells” thing? I feel like this measurement wouldn’t be very consistent across all devices or wouldn’t be a very good general measurement.

Note: might also be beneficial to tell what each measurement is measuring exactly because that would be more helpful. For example, people might not have known about the “render and collision cells” thing for CFrame just as I did not.

2 Likes

I cannot care less about lego doom guy’s opinion.

Just say you made that up.

And you still don’t know what you are saying because a “render cell” (which I am assuming is just a mesh or a part’s geometry) is calculated on the GPU not the CPU (where Luau code is ran), and I’m pretty sure collisions are calculated on a separated thread. So therefore your reason literally is a bunch of crap.

You are right on that but CFrames wouldn’t be the root cause of bad performance if you use them reasonably.


The reason why I say this is useless is because there are way too many holes in it and isn’t even accurate (Look at guy above me explanation).

3 Likes

I used your benchmark module and ran some tests myself and I got pretty different results.

According to this benchmark, CFrame is still high on the list but not nearly as expensive as the previous results showed, only being around x2 as expensive as basic stuff like Name, and BrickColor, etc.

Although, I imagine these results could vary widely on a range of devices.

Here’s the code if anyone wants to check my work:

local Part = workspace.Part
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CreatedParts = {}

local Properties = {
	BrickColor = BrickColor.new("Alder"),

	CastShadow = false,

	Color = Color3.new(1, 0, 0),

	Material = Enum.Material.Neon,

	Reflectance = 1,

	Transparency = 0.5,

	Name = "oof",

	Parent = ReplicatedStorage,

	Size = Vector3.new(1,1,1),

	Position = Vector3.new(1,1,1),

	Orientation = Vector3.new(1,1,1),

	CanCollide = false,

	CanTouch = false,

	CollisionGroupId = 1,

	Anchored = true,

	CustomPhysicalProperties = PhysicalProperties.new(1,1,1,1,1),

	Massless = true,

	RootPriority = 1,

	Shape = "Ball",

	AssemblyLinearVelocity = Vector3.new(1,1,1),

	AssemblyAngularVelocity = Vector3.new(1,1,1),

	CFrame = CFrame.new(1,1,1) * CFrame.fromEulerAnglesXYZ(1,1,1),
}


local Benchmark = {
	ParameterGenerator = function()

	end;

	Functions = {},
}

local Iterations = 100
local SubIterations = 10
local Microseconds = 1000000

for Property, Value in pairs(Properties) do
	Benchmark.Functions[Property] = function()
		local Old = Part[Property]
		
		local t = os.clock()
		for i = 1, SubIterations do
			Part[Property] = Value
			Part[Property] = Old
		end
		return os.clock() - t
		
	end
end

function Benchmark:Run()
	
	local Results = {}
	local Percentiles = {10, 50, 90}
	
	-- Execution
	for Property, Function in pairs(Benchmark.Functions) do
		Results[Property] = {}
		for i = 1, Iterations do
			table.insert(Results[Property], Function()/SubIterations * Microseconds)
		end
		table.sort(Results[Property], function(a,b)
			return a > b
		end)
	end
	
	-- Readout
	print(("Property	%dth	%dth	%dth	"):format(unpack(Percentiles)))
	for Property, Result in pairs(Results) do
		local out = Property.."	"
		for _, Percentile in ipairs(Percentiles) do
			local Index = math.ceil(#Result*(Percentile/100))
			out..=string.format("%.2f μs	", Result[Index])
		end
		print(out)
	end
	
end

return Benchmark
2 Likes

That’s… the point of the post.

Good catch! That was a mistake on my part.

I updated the post to include randomized inputs.

Here are the updated numbers:

image

@AljoSven This is closer to the results you got! However, I would try running your test on the Benchmarker plugin if you would like, since it makes sure tests do not interfere with each other.

1 Like