Behavior change: type() will start returning 'vector' for Vector3

Is something similar like this going to happen to Vector2?

24 Likes

Probably not considering Gui code isn’t as resource intensive and doesn’t require the same level of optimization as manipulating 3d space, especially when done for custom physics simulation, camera movement, internal use in rendering, and a slew of other things in scripts like doing distance checks, dot checks, and probably way more than I don’t even know about.

Edit :

Changed my mind on this, if it isn’t too much effort for Roblox engineers they may as well apply the same optimization considering most of the API looks the same for both value types.

7 Likes

Vector2s are used for much more than just GUI code. I’d like to see something like this optimization occur for it as well.

20 Likes

I agree, I use vector2 quite a lot, and seeing this optimization would be greatly appreciated

5 Likes

How are you able to inspect values for various members across games without also being able to retroactively add new members with different default values on past/future games?

6 Likes

From the original post

Looks like this will happen eventually.

10 Likes

Would love to see this extended to other types, especially Vector2s and CFrames. My current project uses Vector2s extensively for unit positions to save up on network and system memory (it’s an RTS).

4 Likes

How does this effect calling type() on a Vector2?

Couldn’t it be argued that Vector2 and Vector3, despite both being Vectors, are different types? (Maybe I’m overthinking this due to a misunderstanding of Luau)

3 Likes

The only type that will be effected currently is Vector3 (According to @kingerman88’s reply Vector2s/CFrames will receive different optimizations and such, and, Vector2 still returns userdata in the beta)

5 Likes

Currently this has no effect on Vector2 values. Our general recommendation is to use typeof to distinguish Roblox types though; typeof returns “Vector2” for Vector2s and “Vector3” for Vector3s, which will not change.

7 Likes

How come there’s two type functions? I’ve always been confused which one to use

3 Likes

type is the native Lua type function, I believe.
typeof is a custom function implemented by Roblox which returns more specific type info (so for example, typeof(Rect) would return Rect or something whereas type would only return userdata).

5 Likes

How will native vector support affect how much memory a single Vector3 occupies? I assume Vector3 values have some memory overhead to them, so it would be more efficient to store 3 numbers rather than storing one Vector3, right?

[Edit]: I just tested this, and came to an astonishing conclusion:
I ran the following piece of code, which measures how much memory is used to store 30000 numbers from two different methods. In one, I store 30000 entries straight in a table; in the other, I store 10000 entries of Vector3s, which hold 3 numbers each. In theory, the minimum memory occupied by this test should be 8 bytes per number * 30000 = 240000 bytes or 240 kilobytes.

When I first ran the code on the old Vector3s (beta disabled), I got 608 KB for the vector3s and 512 KB for the numbers. Then, when I enabled the beta and ran the test again, I got 256 KB for the vector3s and 512 KB for the numbers. This seems to indicate to me that Vector3s are now super efficient for storing number data.

I tried out the test with different quantities, which resulted in Vector3s always being much better than numbers. Strangely, I kept encountering very nice looking numbers like 4096 or 8192 or 1024. It is a bit concerning, because the perfectness of these numbers suggests something is incorrect about the way I measured the results. Can someone try this test and confirm my results?

local n = 10000
do
local start = collectgarbage(“count”)
local s = {}
for i = 1, n * 3 do
s[i] = math.random(-1000, 1000)
end
print(collectgarbage(“count”) - start)
end

wait(10)

do
local start = collectgarbage(“count”)
local s = {}
for i = 1, n do
s[i] = Vector3.new(math.random(-1000, 1000), math.random(-1000, 1000), math.random(-1000, 1000))
end
print(collectgarbage(“count”) - start)
end

[edit #2]:
Vector3int16 does not appear to be native-ized, and it is only marginally better in terms of memory used than the old non-native Vector3s. Very surprising.

6 Likes

A Vector3 conceptually would be stored using only 3 numbers in an internal structure so in terms of memory usage, I would assume that there wouldn’t be much of a difference unless there was some other internal memory optimizations related to how Lua works itself, or they are doing some structure packing that wasn’t done before.

The main important change is that Vector3 was a heap-allocated object, which means everytime you created one the memory had to be allocated dynamically, which has a decent performance overhead compared to non-heap objects, especially in tight loops. With the new update, a Vector3 will be a value type like a number or boolean. For example, you don’t expect a number to be dynamically memory allocated, its just a value. It doesn’t have dynamic size. A Vector3 will now be just like storing 3 numbers. Though, I expect that there is some specific optimization cases for the new Vector3 and you should use it instead of using 3 separate numbers.

Those are some good results in your test though. I would suggest doing some speed tests as well and see what the differences are.

3 Likes

It turns out that Vector3s actually lose precision over just plain numbers, so it does explain why even on the old Vector3 system, you could see the Vector3s occupying less memory than the numbers counterpart by like 25% or so (depending on what quantity of values you test).

But when I compare the old Vector3s to the new Vector3s, the new Vector3s are always using significantly less memory than the old ones, and the precision appears to be the same.

I am aware of the major performance improvements and will definitely be getting huge leaps in performance from it, but I also store a lot of data in memory (250 MB of it, actually, in script memory), so this will also help with that.

4 Likes

That’s interesting. Must be some sort of hashing-like optimization that can result in collisions sometimes. Maybe you only have to store the length and direction of the vector, and the components can be determined from that (with minor precision loss).

That’s good to hear. I don’t store that much Vector3s in memory for an extended time, so I’m mostly excited about the performance improvement, but thats good to know in the future if I do end up storing them.

5 Likes

Yes, that is the case. In fact, all value types take the same amount of space to store, so a Vector3 and a number take up the exact same amount of space now.

However, you have to be careful here: Yes, storing your number data in Vector3s is now “super efficient”, but only space wise. If you actually store your numbers like this, you’re also going to have to write code that gets and sets them at some point in order to use them, and that code will become significantly less efficient performance wise, because it has to take apart Vector3s and put them back together again, on top of doing a non-trivial calculation to find the index in the table.

So, you’d have to have a pretty unique scenario where you’re storing a ton of temporary data, and only accessing a very small portion of it to get a benefit from packing number arrays into Vector3s.

5 Likes

Luckily I hit that exact use case! I have 250+ MB of map data that is loaded up into a gigantic table on runtime, which is then read throughout gameplay for access to collision data. The data itself represents 3D points so Vector3 is its natural state, but previously I had broken them down into components for both storage and later calculations b/c old Vector3 being bad.

Let’s just say this is a huge life saver in memory efficiency, speed (which is important at the scale of calculation I do every frame), and convenience of not having to read all of my equations with a gazillion xyz variables. TYSM!

3 Likes

If you’re only reading the data and not writing it at runtime, you may actually be even served better by using a string for this. That will give you 100% memory efficiency packing your data into memory, vs even with Vector3s I think you’ll still “only” get ~50% memory efficiency.

For instance, to extract the data with str:sub(index*4, index*4 + 3) and then use string.byte to turn the data back into numbers.

3 Likes

Isn’t this what string.unpack is for?
string.unpack("f",str,(index-1)*4+1) would be an example for 32 bit floats

It also seems weird to get a substring to just use string.byte on that substring, as string.byte accepts bounds to get several bytes at once.

Storing numbers as a string can take advantage of specific properties to reduce the space taken, like if the number is an integer and within a small range then it could be represented with a byte or two.

2 Likes