Luau Native Code Generation Preview [Studio Beta]

i have this gigantic script
it (along with it’s modules) are the only scripts in the game and they all have the native tag
i have met all the requirements shown in original post, and strict typechecking told me there was no type errors

for some reason it fails, giving me this warning:

my guess is that the script is simply too big

3 Likes

Yes, this error means that we have hit a limit of memory allowed for a single script.

Right now we recommend splitting scripts into smaller parts, in the future we might revisit the limits.

5 Likes

How does it work with module scripts? I tried it on my Particles Collisions Module but i see no difference in performance. Should i put -!native in ModuleScript or Script? Or maybe in both?

3 Likes

Very useful feature indeed. Faster computing were exactly what I’ve been waiting for on Roblox!

--!native
local TimeBegin = os.clock(); --> Benchmarking.

local Primes = {2, 3};

local function isPrime(x: number)
	for Index: number = 1, #Primes do
		if x % Primes[Index] == 0 then
			return false;
		end
	end

	Primes[#Primes + 1] = x;

	return true;
end

local function Calculate_nth_Prime(n: number)
	local i = 5;

	repeat
		isPrime(i);
		i += 1;
	until #Primes > n

	return Primes[n];
end

local Prime = Calculate_nth_Prime(25000); --> Calculating the 25000th prime number!

print(
	("Prime number is %d operation took %.5fs"):format(Prime, os.clock() - TimeBegin)
);

Here are the results I’ve got from my budget CPU:

Native Output: Prime number is 287117 operation took 4.85615s

Default Output: Prime number is 287117 operation took 9.24579s

Luau code calculating 25000th prime number gets about 2X faster with native code execution!

From what I understand from this post; certain sections of our code, specifically those that do not rely on tables, Lua C functions, and the Roblox Engine, are compiled into a low-level assembly-like code for computation.

Please correct me if you can :+1:

2 Likes

Hello, do you guys have a date for when the beta going to move to full release? I have many projects where this feature would help a ton

7 Likes

A really cool new feature. From my testing the compiler likes buffers a lot it makes it a lot more quicker when I use buffers instead of vectors and variables with some expections.
There could be more features that allow you to tell the compiler what to expect I can’t wait to use this with the new editable mesh/images

1 Like

Do you know why relying on tables will not make native Lua as effective? Is it because tables are already executed in roughly the same C code, regardless of whether or not you have native Lua enabled? Thanks.

1 Like

I don’t think this was mentioned in this post, but apparently the native code generation does not work in the command line using loadstring, but it does work when in Run mode on the server:

ON THE SERVER (Run mode)
Screenshot 2024-01-10 at 7.36.54 PM

IN THE COMMAND LINE
Screenshot 2024-01-10 at 7.36.37 PM

Code (exact same for native and non-native):
Screenshot 2024-01-10 at 7.43.50 PM

1 Like

Is it still using loadstring when in run mode, or is the module required instead? Using loadstring in general disables a lot of Luau optimizations. It makes sense that it also won’t benefit from native compilation.

1 Like

neither. It’s a server script which runs right after entering Run mode (by pressing Test > Run). But you’re right that --!native does not work for scripts that loadstring was used on.

1 Like

:red_circle: Hello. I have noticed a very huge problem when --!native is enabled. Namely, writing to a buffer a region whose end “touches” the buffer’s end will cause a slow down of TWO times for some reason, and if that region does not touch the end, it runs the function at regular speed. This happens regardless of whether i8, u8, i16, u16, i32, u32, f32, or f64 is used (I’m referring to the buffer.writeXXX functions):

Code:

--!native

local function time(func, label)
	local t1 = os.clock();
	func();
	local timeDif = os.clock() - t1;
	warn(if (label ~= nil) then `{label}: {timeDif}` else timeDif);
	return timeDif;
end

local length = 10_000_000;

------------------------------------------------------------------------------
--// Buffer assignment (touches end of currentI buffer)

local bytesPerElement = 4;
local size = length * bytesPerElement;
local allBytes = buffer.create(size);
local currentI = buffer.create(13 + bytesPerElement); -- extra 1 is *not* added

time(function()
	for i = 0, size - 1, bytesPerElement do
		buffer.writeu32(currentI, 13, buffer.readu32(allBytes, i));
	end
end, "native A")

------------------------------------------------------------------------------
--// Buffer assignment (does not touch end of currentI buffer)

local bytesPerElement = 4;
local size = length * bytesPerElement;
local allBytes = buffer.create(size);
local currentI = buffer.create(13 + bytesPerElement + 1); -- extra 1 is added

time(function()
	for i = 0, size - 1, bytesPerElement do
		buffer.writeu32(currentI, 13, buffer.readu32(allBytes, i));
	end
end, "native B")

------------------------------------------------------------------------------
--// Normal assignment

local allBytes = table.create(length, 0);
local currentI = 0;

time(function()
	for i = 1, length do
		currentI = allBytes[i];
	end
end, "native C")

As you can see, native A’s for loop takes 0.08817541666212492 seconds (~ twice as slow as native B), native B’s for loop takes 0.039451791657484137 seconds, and native C’s for loop takes 0.03687004165840335 seconds.

1 Like

What is the device you are testing this on?

On AMD PC, there is no difference between A and B.

1 Like

Interesting. I’m using an Apple Mac Mini, M2 chip with 8-core CPU and 10-core GPU, and 16 GB of RAM.

1 Like

Actually, this seems to very likely be an Apple-silicon problem. I tested this on my old Intel 2018 13" MacBook Pro with Intel Iris Plus Graphics 655 and 8 GB of RAM, and like you, there was no difference between A and B.

Screenshot 2024-01-11 at 2.07.59 PM

1 Like

Did you guys experience the same issue with Apple Silicon computers? A fix would be appreciated.

1 Like

Thanks for the info, we have found the source of the issue on Apple HW.

3 Likes

How am I just knowing about this now! This is so cool! So basically luau is the same as the native script beside native script having faster times and luau supporting more devices? Are there any other differences?

1 Like

Weird issue where when I do some specific stuff my buffer variable holds a different variable.
I really don’t understand what’s causing it.

--!native

local Buffer = buffer.create(1);

buffer.writeu8(Buffer, 0, 0); --// We need to write to the buffer or else it doesnt work

local Var1;
local Var2 = "Hello, World!";

--[[
	having "Var1" and "Var2" above causes the "Buffer" variable to hold "Var2" value.
]]

local function Bug()
	--// If done right (what is even the right way) our "Buffer" variable now holds "Hello, World!"
	
	if type(Buffer) ~= "buffer" then
		warn("Buffer new value:", typeof(Buffer), Buffer);
		error("Buffer is no longer a buffer!!", 0);
	end

	--// 2 lines of code below here actually doesnt matter but "Buffer" variable doesnt change when we remove anything below here.
	repeat pcall(1) until true;
	Var1 = nil;
end


--// Doesnt make much sense but we also need this in a loop
repeat
	Bug();
until true

if type(Buffer) == "buffer" then
	--// Warn in console if our variable did not get changed
	warn("Code ran as expected. (variable was not modified)");
end

Code works as expected without native execution.
As you can also probably tell I found this completely on accident so the code is kinda messy.

My CPU is AMD FX-8300 overclocked to 3.8GHz

I’m so confused :sweat_smile:

1 Like

It’s a bug, thank you for the report.
We’ll fix it with a release in a few weeks.

1 Like

Good that roblox knows Compiled > Interpreted