[VIDEO] How to not use WHILE WAIT() loops

Why would there be a need to benchmark this? It’s obvious that thread creation is expensive, quite significantly amount of work has to be done to create a new thread.

Only create new threads when they are really necessary, creating a new thread just to yield is unnecessary and unnecessarily expensive as well.

The code by @OP is not optimal, OP is creating a new thread which is unnecessarily expensive and will not even work properly to begin with.

Also, please read my post clearly, I never said that tick is deprecated, it will be soon. This information is available on the os library.

No, Connect calls the callback argument passed to it in a new thread. Since it’s connected via Heartbeat, it will call the callback every time heartbeat fires.

You always need to back up your points with evidence, whether or not you think it’s true.

I never said that tick() was deprecated?

1 Like

Here’s a benchmark of creating a thread. It’s not expensive at all.
(remember that the 50th percentile is more important than the max and average! outliers can cause those numbers to be very high, the 50 percentile gives the most accurate representation of how fast/slow the benchmark is)

Code
--[[

|WARNING| THESE TESTS RUN IN YOUR REAL ENVIRONMENT. |WARNING|

If your tests alter a DataStore, it will actually alter your DataStore.

This is useful in allowing your tests to move Parts around in the workspace or something,
but with great power comes great responsibility. Don't mess up your stuff!

---------------------------------------------------------------------

Documentation and Change Log:
https://devforum.roblox.com/t/benchmarker-plugin-compare-function-speeds-with-graphs-percentiles-and-more/829912/1

--------------------------------------------------------------------]]

return {

	ParameterGenerator = function()
		-- This function is called before running your function (outside the timer)
		-- and the return(s) are passed into your function arguments (after the Profiler). This sample
		-- will pass the function a random number, but you can make it pass
		-- arrays, Vector3s, or anything else you want to test your function on.
		return 
	end;

	Functions = {
		["coroutine.wrap()"] = function(Profiler, RandomNumber) -- You can change "Sample A" to a descriptive name for your function
			-- The first argument passed is always our Profiler tool, so you can put
			-- Profiler.Begin("UNIQUE_LABEL_NAME") ... Profiler.End() around portions of your code
			-- to break your function into labels that are viewable under the results
			-- histogram graph to see what parts of your function take the most time.
		
		
			-- Your code here
			
			coroutine.wrap(function() end)()
		end;

		-- You can add as many functions as you like!
	};

}
2 Likes

This is possibly one of the most ignorant things to say when getting yourself involved in a debate, to be perfectly honest. Why would you put a claim out there if you can’t back it up?

This is especially stupid, considering that:

It’s not actually expensive.

Even further, I’ve used my methods countless times. I’ve created Yucon Framework, which runs games like Caliber Gun Demo. Both instances are optimized much better than an average game, so I can confidently say that threads don’t cause issues.

4 Likes

Threads don’t cause issues, but that wasn’t my point. Thread creation is expensive and thread creation in Lua is done internally in C, but since thread creation in C is fast, there won’t be any notice of expensiveness really but it is still something to take caution about.

For example:

Thread creation in other languages like Java is pretty expensive.

The benchmark shows it’s not expensive. Why are you continuing to say it’s expensive and that it should be worried about when it’s already been proven that it’s not expensive?

I don’t see how this is related to the conversation. We’re talking about Lua, not Java.

2 Likes

The benchmark shows it’s not expensive. Why are you continuing to say it’s expensive it’s not expensive?

No, thread creation in C is not expensive, but in other languages, it is. Creating a new thread in Lua, that is done internally in C.

and that you should be worried creating threads

Had gone a little off-topic there, my bad.

I don’t see how this is related to the conversation. We’re talking about Lua, not Java.

That is just an example, thread creation in C is not expensive, but that doesn’t mean that thread creation in other languages isn’t expensive which you’re tried to prove but failed. “thread creation is not expensive”, should have said it as “thread creation in Lua © is not expensive but in other languages it is”.

Threads from other languages shouldn’t have been brought into this conversation in the first place. Lua is what matters here because it’s what the original post is about.

1 Like

That’s right, the only reason I said it was due to the fact that you said “thread creation is not expensive” which only applies to Lua but it confuses the reader that you’re actually saying that “thread creation is not expensive for any language”.

Yes there is nothing wrong with wanting more reliable wait times. But blindly replacing all waits with a runservice based counterpart is not a good idea to go about it. And if you get a wait with 1 second extra of wait time then you are doing something wrong. When I picked a one of the Roblox studio place templates and started a local server with 9 players and my FPS dropping to 3 I still only got a wait delay of 0.1 seconds.

And even if you get a 1 second delay you can just mitigate it by replacing all spawn(FUNC) functions with corotoune.wrap(FUNC)() and using deltas and replacing wait(0) and wait() with Runservice.Hearbeat:Wait()
And for sure if you use a wait with a delay of less than 1 seconds then you can use a hearbeat based wait replacement.

But for all the cases where you use a wait delay of 1 or longer and you cannot use a delta then it doesn’t really matter. It doesn’t matter if a zombie ragroll takes 10 seconds instead of 9 seconds to despawn.

In one of my worstly optimised games the wait delay only affects things which do not use time deltas. And the source of the lag was that I was using busy waits and spawn() when I made all the busy waits event based and replaced spawn() as well as started using time deltas all my problems went away.

That’s a stupid argument - just because something can be less important, doesn’t mean it should be ignored. Also, the reason yielding can become offset by 1 second or above as @LexiDog5 mentioned is because when you yield in several scripts, the task scheduler clogs up extremely easily, thus causing slow update times with yields - even having 100 simultaneously yields can cause issues, which isn’t as much as you think. Utilizing a custom task scheduler is common practice among expert programmers.

1 Like

Why are you discussing other languages? This seems irrelevant to the topic at hand here, especially since both the post and the forum are dedicated to Lua in the first place.

This isn’t exactly a great example, you can use debris service to despawn. Using any yielding to despawn is an awful idea.

That’s not true. Because every script is a psuedo-thread, wait pauses that thread. Using a runservice counterpart prevents that thread from being paused, hence the accuracy and optimization.

I don’t know why are you cluttering the thread for irrelevant posts when I already answered this.

That’s right, the only reason I said it was due to the fact that you said “thread creation is not expensive” which only applies to Lua but it confuses the reader that you’re actually saying that “thread creation is not expensive in any language”.

Yes. But so does EVENT:Wait().

Also what I meant with blindly replacing waits with a runservice counterpart is doing wait = function(t) local start = os.clock() while os.clock() < start + t do Hearbeat:Wait() end end or something simular.
Of course replacing while true do wait() --[[somecode]] end, while true do wait(0) --[[some code]] end or while wait() do end with Hearbeat:Connect(function() --[[somecode]] end) is great and absolutely should be done. But replacing the default wait function with something where the code checks each event that enough time has passed is not good if the time you are going to wait is 1 seconds or more.
Think about it. Is it smart checking if enough time has passed 60 times a second. Robloxes default wait only checks at 30hz (half of runservice events) for it and C code generally is more optimised than lua.

My point is that you absolutely should use events and the runservice events are good and should be used. But just replacing the wait function blindly is not. You should use something else than Robloxes default wait when the wait time is less than 1 seconds. But using wait(n) and all the functions that depend on it(expect spawn never use spawn) is ok if the time waited is 1 seconds or more.

Minor corrections:

…not sure if you’re aware but unfortunately Debris uses the Task Scheduler too. :slightly_frowning_face:

A better way to describe this would be that wait() uses the Task Scheduler, and the task scheduler isn’t guaranteed to stop the wait from yielding in time, especially if there’s a large amount of waits or lots of other tasks being done at once.

I’m not going to continue to reply here since I feel that this is getting nowhere.

2 Likes