samufi
samufi
MModular
Created by samufi on 1/9/2025 in #community-showcase
Larecs: a performance-centred archetype-based ECS
We have built an archetype-based ECS, mostly building on the Arche ECS in Go lang. There are still a couple of features on the road map, but the central functionality is there. In benchmarks, Larecs beats Arche in almost all test cases :-). Have a look here: https://github.com/samufi/larecs I would be super happy if others were interested in joining, and I'd greatly appreciate feedback and ideas how to bring SIMD and other Mojo perks to the world of ECS.
16 replies
MModular
Created by samufi on 12/2/2024 in #questions
Is unpacking variadic arguments coming soon?
At the moment, variadic arguments can be passed to other functions via VariadicList and the like. However, this approach fails if the second function is more general. An example:
@value
@register_passable("trivial")
struct MyType(Stringable):
fn __str__(self) -> String:
return "MyType"

fn do_stuff(self):
pass

fn func2[T: Stringable](args: VariadicListMem[T]):
for i in range(len(args)):
print(str(args[i]))

fn func1(args: VariadicListMem[MyType]):
for arg in args:
arg[].do_stuff()
func2(args)

fn func(*args: MyType):
func1(args)
@value
@register_passable("trivial")
struct MyType(Stringable):
fn __str__(self) -> String:
return "MyType"

fn do_stuff(self):
pass

fn func2[T: Stringable](args: VariadicListMem[T]):
for i in range(len(args)):
print(str(args[i]))

fn func1(args: VariadicListMem[MyType]):
for arg in args:
arg[].do_stuff()
func2(args)

fn func(*args: MyType):
func1(args)
Here, args is a VariadicList and cannot be passed as VariadicListMem. Using VariadicListMem in func2 does not work, however, since Stringable is not register passable. Making a register passable Stringable trait, in turn, is not possible, because AnyTrivialRegType is not trait and cannot be inherited from. I run into a similar issue if I use the (actually preferred) version that is fully parametric:
trait MyTypeTrait(Stringable):
fn do_stuff(self):
...

@value
struct MyType(MyTypeTrait):
fn __str__(self) -> String:
return "MyType"

fn do_stuff(self):
pass

fn func2[*Ts: Stringable](args: VariadicPack[_, Stringable, *Ts]):
@parameter
for i in range(args.__len__()):
print(str(args[i]))

fn func1[*Ts: MyTypeTrait](args: VariadicPack[_, MyTypeTrait, *Ts]):
@parameter
for i in range(args.__len__()):
args[i].do_stuff()
func2(args)

fn func[*Ts: MyTypeTrait](*args: *Ts):
func1(args)

fn main():
func(MyType(), MyType())
trait MyTypeTrait(Stringable):
fn do_stuff(self):
...

@value
struct MyType(MyTypeTrait):
fn __str__(self) -> String:
return "MyType"

fn do_stuff(self):
pass

fn func2[*Ts: Stringable](args: VariadicPack[_, Stringable, *Ts]):
@parameter
for i in range(args.__len__()):
print(str(args[i]))

fn func1[*Ts: MyTypeTrait](args: VariadicPack[_, MyTypeTrait, *Ts]):
@parameter
for i in range(args.__len__()):
args[i].do_stuff()
func2(args)

fn func[*Ts: MyTypeTrait](*args: *Ts):
func1(args)

fn main():
func(MyType(), MyType())
Here, the general function cannot take the more specific variadic pack. I understand if everyone is tired of the "when will XY be implemented" questions. However, for me the question is whether I have to build a big workaround (if it takes longer) or simply use duplicate code (if this can be fixed soon). Any advice (or alternative solutions)?
2 replies
MModular
Created by samufi on 9/23/2024 in #questions
What does the keyword `capturing` do?
I noticed that the keyword capturing is necessary when I want to time code via time_function. For example,
from time import time_function

fn closure() capturing:
my_function()

print(time_function[closure]() * 1e-9)
from time import time_function

fn closure() capturing:
my_function()

print(time_function[closure]() * 1e-9)
What is going on under the hood if I am using the keyword capturing? I have not found documentation on this.
2 replies
MModular
Created by samufi on 9/21/2024 in #questions
How can I use a fixed-size integer type to index a list?
I want to use a fixed-size integer value as index of a list. However, I get the "no matching method in call to 'getitem'" error.
var l: List[Int] = List[Int]()
l.append(1)
var ind: UInt32 = 0
print(l[ind])
var l: List[Int] = List[Int]()
l.append(1)
var ind: UInt32 = 0
print(l[ind])
How can I use a 32 bit unsigned integaer such as ind in the example above as index for a list (preferably without performance penalty and lengthy additional syntax)?
7 replies
MModular
Created by samufi on 8/5/2024 in #questions
Is there a (N-D) contiguous data structure for non-trivial structs?
I need a data strucuture that is a 2-dimensional array of (inline) lists of pairs. That is, if subdata = data[i][j], then subdata is a dynamic data structure that contains tuples of size 2. My take on solving this was to use a Tensor for the 2-D data structure, an InlinedFixedVector for the flexible part, and a StaticTuple for the pair.
from tensor import Tensor
from collections import InlinedFixedVector
from utils import StaticTuple


struct MyStruct:
var data: Tensor[InlinedFixedVector[StaticTuple[Float64, 2], 10]]
from tensor import Tensor
from collections import InlinedFixedVector
from utils import StaticTuple


struct MyStruct:
var data: Tensor[InlinedFixedVector[StaticTuple[Float64, 2], 10]]
However, I get the following error:
'Tensor' parameter #0 has 'DType' type, but value has type 'AnyStruct[InlinedFixedVector[StaticTuple[SIMD[float64, 1], 2], 10]]'
'Tensor' parameter #0 has 'DType' type, but value has type 'AnyStruct[InlinedFixedVector[StaticTuple[SIMD[float64, 1], 2], 10]]'
I figure that Tensors can only hold trivial data types. If so, is there a good general data structure for multi-dimensional data? I was thinking of putting InlineList into a self-made 2D represntation of List, but InlineList does not implement CollectionElement (as of yet?).
6 replies
MModular
Created by samufi on 8/2/2024 in #questions
How can I write asynchronous code?
How can I implement asynchroneous code in mojo (using async etc.)? In the Changelog, I find the following example (slightly adjusted):
async fn add_three(a: Int, b: Int, c: Int) -> Int:
return a + b + c

async fn call_it():
var task = add_three(1, 2, 3)
print(await task)
async fn add_three(a: Int, b: Int, c: Int) -> Int:
return a + b + c

async fn call_it():
var task = add_three(1, 2, 3)
print(await task)
This gives me the following compiler error:
test_async.mojo:39:17: error: 'Coroutine[Int, {}]' is not copyable because it has no '__copyinit__'
print(await task)
^~~~
test_async.mojo:39:17: error: 'Coroutine[Int, {}]' is not copyable because it has no '__copyinit__'
print(await task)
^~~~
I do not understand this error, as I thought that Coroutine is register passable (as visible in the source code). What works is
async fn add_three(a: Int, b: Int, c: Int) -> Int:
return a + b + c

async fn call_it():
var task = await add_three(1, 2, 3)
print(task)
async fn add_three(a: Int, b: Int, c: Int) -> Int:
return a + b + c

async fn call_it():
var task = await add_three(1, 2, 3)
print(task)
However, if I always put await on the right hand side, I will never be able to execute code asynchroneously, as I am always waiting until the line has finished. What am I doing wrong / where is my misconception? (Or is this simply a bug?) In the changelog it furthermore says that tasks can be "resumed" after completion (see here). What is meant by "resume" and how do I do that? I am working with the nightly built mojo 2024.7.2005 (96a1562c). Addon: I asked this question on the Q&A page and on SO some time ago. Sorry for the double posting; I just do not know what is the best way to get in touch with people who can help. I will keep the copies up to date (or delete them if people think this would be better).
11 replies