Is wrapping "do end" in code a good practice

Code 1:

do

    local plate = workspace.Baseplate

    for i = 1, 100 do 
        plate.BrickColor = BrickColor.Random()
        print(plate.BrickColor)
    end

end

Code 2:

local plate = workspace.Baseplate

for i = 1, 100 do 
    plate.BrickColor = BrickColor.Random()
    print(plate.BrickColor)
end

Which is more efficent, and why? When should I use do end?

3 Likes

It doesn’t really make a difference since it does it all in milliseconds of it being executed. If you calculated it I believe that Code 2 would probably be a little bit faster because it doesn’t have to register another end to the script.
It’s mainly a preference on how you like to script; just be consistent.

5 Likes

They both have the same timing, if not one will be off by nanoseconds.

Why are you asking this anyway? Don’t micro-optimize your code until you absolutely desperately have to. There are faster ways, such as keeping BrickColor.Random in a local variable beforehand, or something along those lines.

In response to your second question, use do end whenever you feel like. Seriously. I use it for when I just want to collapse code. The only time you’d ever NEED to use it is if you hit the 200 local variable limit, which most people never do.

8 Likes

Going off of this, a small script like this won’t have a big impact on your game unless it’s part of a much larger script itself. You really wouldn’t need to optimize your scripting to this level is if you hit a high local variable limit.

As I was typing this your post expanded and said exactly this. Sorry ):

3 Likes

This might serve of some help;
https://devforum.roblox.com/t/why-i-dont-sleep-micro-macro-optimizing-mega-guide/71543

TL;DR it has absolutely no effect (not even a tiny one); do end does not compile to bytecode.

4 Likes

The only practical reason I’ve ever done this is when I’m allocating a lot of memory and want to make sure it can be freed while the script continues on. In the example you provided, you’re just reducing readability with no actual benefit.

9 Likes
I believe the only reliable way to get this is to either set your local variables that hold large amounts of state to nil, or make sure they have a separate stackframe (that is, call a function). do..end by itself is not enough - local variable storage in Lua is disjoint, it won't share local space from one block with another, and GC disregards live ranges for locals. ^ this was incorrect, disregard

Actually, as it happens Lua has an interesting way of handling this.
As an example, a local variable defined within a do end block will die after the end unless CLOSEd by a function using it as an upvalue (in which case it’ll just be a floating reference with the function). You can observe this yourself with things such as:

do
    local a = 1
end

local b = 5 + x

The global x will be stored in the same register as a once was before going on to the math operation.

The reference will be gone for the original a, and gc will do its job.

2 Likes

Ah, you’re right. So you might get lucky and get another local/temporary stored in the same slot, and you might not get lucky.

Yeah, due to debugging collisions b can’t be stored there, but it’s free for whatever temp registers need be used. What I’ve found most intriguing about this is the fact that most “scoping” actually happens at compile-time, with functions being the only real bytecode scopes.

You’re all crazy… trying to optimize your Lua registers is going to make absolutely zero difference whatsoever even in the tightest of loops. It’s literally just a slightly different offset in the VM instructions somewhere unless you happen to exactly straddle an allocation size boundary (Assuming that Roblox uses some optimized-for-games highly bucketed allocator), and even then the size of an allocation doesn’t correlate that much with how long it takes.

And furthermore unless you’re writing some really tight-loop Lua-side code like pathfinding or non-trivial AI logic, the second you start touching the Roblox API those calls will take far more time than any of your Lua-side code was.

Just write your code to be nice and maintainable / understandable and you’ll be thanking yourself later.

13 Likes

i never even heard of “do” standing alone in a script… soooo

I wouldn’t worry about the speed difference at all.

I just use this because of OCD—like, if a local variable only belongs in a place where it is calculated once, and has no use outside of that context, I wrap it up in a “do” statement.

I think of “do end” statements as mini-functions that you are only going to call once, so there’s no use in actually creating a function. Though it’s logically needless, it does help a little bit for readability and obscure debugging scenarios, since if you’re only using a variable once in a small block, you are able to read the code and understand that this variable only has a use within that block. Also, if you are debugging multiple blocks with intermediary variables like that, and you happen to have a value that should be “nil” but isn’t because the variable name was incidentally assigned beforehand, it might also cause a problem.

Mostly, though, it just feels nice to think that it will get garbage collected like it should. A sense of thoroughness, like you’ve disposed of your variables like a decent human being.

4 Likes

They’re used for controlling scope.

do
    local x = 0
end

print(x) --nil
5 Likes

I use do ... end if I’m going to, e.g., define an inline “library” and want to be sure to encapsulate variables that I don’t believe the surrounding code deserves access to. Something like:

local MyLib; do
    local globalState;
    function MyLib.doSomething()
        ...
    end
end

Then I can access MyLib.doSomething() later on, and I can code MyLib.doSomething as if nobody will be touching globalState except for me.

Also, I love using the code folding feature (of some editors; roblox’s is totally borked), so adding foldable sections for when even the fully-folded script is too long to easily glance through is nice.

5 Likes

There is no real difference other than organisation and a e s t h e t I c

The discussion seems to have veered into the realm of micro-optimisation, but I think the main advantage of scoping in do-end blocks is that you avoid polluting the script environment, so you can use short and descriptive variable names multiple times in a script without having to think of synonyms or appending words.

1 Like

I use it in the case where I need to break up a long LocalScript into groups.
I had this issue in particular with Super Nostalgia Zone’s main menu. I ended up exceeding the 200 local variable limit because I wasn’t putting stuff into local scopes.

3 Likes