Update [1.0.8]
- Addressed issue where a yield wouldn’t work properly (@GrayzcaIe)
- Optimization of code to function faster (I’ll show a benchmark to compare all solutions in a moment)
Glad you figured out a solution. I will check it out when I get back on. But otherwise, exceptional job making this module. Looking forward to more of your potential projects.
So I wrote my own custom yield script, just for fun, and after doing a benchmark it seems to be functioning better than this one.
Benchmark script:
-- // By StrategicPlayZ \\ --
local FastWait = require(workspace.FastWait)
local CustomWait = require(workspace.CustomWait)
for i = 5,1,-1 do
print("[STARTING BENCHMARK IN " .. i .. "s]")
wait(1)
end
local total = 50000
local wait_time = 1
local benchmark_func = FastWait do
print("...[BENCHMARK BEGIN]...")
local num_failed = 0
local total_wait = 0
local highest_wait = 0
local lowest_wait = math.huge
local completed
game:GetService("RunService").Stepped:Wait()
for index = 1, total do
coroutine.wrap(function()
local taken_time = benchmark_func(wait_time)
if ((taken_time > (wait_time + 0.04)) or (taken_time < (wait_time - 0.04))) then
num_failed += 1
end
if (taken_time > highest_wait) then
highest_wait = taken_time
end
if (taken_time < lowest_wait) then
lowest_wait = taken_time
end
total_wait += taken_time
completed()
end)()
end
do
local n = 0
function completed()
n += 1
if (n ~= total) then return end
local average_wait = (total_wait / total)
print("[% NUM FAILED]: " .. (num_failed / total * 100) .. "%")
print("[AVERAGE WAIT]: " .. average_wait)
print("[HIGHEST WAIT]: " .. highest_wait)
print("[LOWEST WAIT]: " .. lowest_wait)
end
end
end
Result (custom wait):
Result (my wait module):
After slightly altering my code, the end result was this:
All I altered was the for loop in the Stepped
event, editing it from 10,000 to 100,000. By doing so, it effectively unyielded all the yields at once, rather than taking 5 Stepped
events to unyield them, since it only unyielded a maximum of 10,000 per Stepped
. I set this limit with script exhaustion time in mind, which your script does not take into account for. By raising the for loop limit, this module effectively addresses the flawed benchmark.
Either way, I will not be raising the unyield limit from 10,000 since as stated, it could cause havoc when you have 100+ while true do wait() end
loops.
I set mine to 100k, it still shows similar result, I even set it to while true loop, it still shows some fails.
By the way forgot to mention I have edited the module with os.clock instead of time since it is much more accurate.
This is the result when I changed os.clock back to time().
You too try with os.clock instead.
Here is the result of my module:
And no it’s not os.clock being inefficient, as you can confirm it from the following code in the command bar:
(Run this when the game is running.)
local a = time()
print("a")
print(time()-a) -- OUTPUTS 0s
local a = os.clock()
print("a")
print(os.clock()-a) -- OUTPUTS SOMETHING DIFFERENT, IN MY CASE: 0.000154
This seems like an issue of having different hardware in that case. I don’t really bother comparing stuff at such a level, since it’s very prone to illusionary and misleading results - the only reason I compared this module with Roblox’s is that the margin of error is small enough in comparison to Roblox’s “failure rate”, and as such the margin of error can be ignored completely. With modules that provide very similar results with only the slightest differences, it’s hard to say what’s truly the better option. I’ll run both scripts though the micro-profiler and also boatbomber’s benchmarker plugin instead, as they provide with more accurate results.
So I tried to use it CustomWait() instead of CustomWait(.1) on while true loop and my Computer hated me, should definitely add a default minimum
…Could you elaborate? If no time is added, it will yield for 1 Stepped
frame before unyielding shortly after.
Weird, I did this code and it exhausted the script
while true do
wait()
-- Code
end
That’s weird. I’ve confirmed this behavior just now, and it does seem like wait(0) or wait() achieve this effect. An easy solution would be to just do wait(0.01) for now. I’ll fix this next update and clamp the minimum value to 0.01.
That doesn’t happen with my module. Seems like using a binary heap is not a good way of implementing custom yields.
Does your post have any other relevancy to this discussion other than to brag and advertise your own module?
It isn’t bragging. I am just saying that your module isn’t the best solution to yielding (as it claims in the title), nor do I claim mine is the best.
Best != perfect. Don’t mix up the words. Thus far I have not seen any solution which other modules offer better solutions than this module. You mentioned your own module with no other relevancy other than to imply it is better than mine because the issue mentioned above does not occur with yours. Before you state that you didn’t say that, I meant imply. Intent is enough, and saying that was not your intent would be delusional.
According to my benchmarking the module of CloneTrooper mentioned in the main thread performs better than this one.
This loops us back to the previous debate - code performs and scales differently on different machines. There is no one absolute, and I’m not that interested in results that could be biased depending on what machine it was ran.
Either way, since both utilize custom task schedulers they are already optimized out well enough. The only difference you would see between the two would be when running both > 10^7 times, which would never occur in any productive code.
Also, sorry for being rude in the previous reply. I was rather heated towards the reply.
That’s a satisfactory reply. Thanks.
I have found a bug (in Roblox).
When you use this module, you can not stop it from the outside, i.e deleting/disabling the script will not stop it.
Also when you are using custom wait modules the Script Performance tab does not show the usage % of it.
We can fix this by checking if a script is deleted or disabled in loops etc. This bug is probably because of how Roblox works internally.
EDIT: Tested it again, it stopped this time. The outcome is variable, or it is dependent on the code in the loop.
I mean… I’m unsure as to why you’d wanna do that, but a quick workaround would be:
local Yielded = {}
local function WrappedWait(n)
Yielded[coroutine.running()] = true
local a,b = RBXWait(n)
Yielded[coroutine.running()] = nil
return a, b
end
local function UnyieldAll()
for Thread in next, Yielded do
coroutine.resume(Thread)
end
table.clear(Yielded)
end