Passing a Slice to a function

What is happening when I pass a slice of a list to a function? With these examples (very contrived, but reflective of what I'm seeing in larger real code), passing a slice of a list to a function of signature borrowed items: List[UInt8] is waaaaayyyy slower than any other way. Is it allocating a new copy on a slice? Should I be looking into Span's instead? Or is this an area still being looked at https://github.com/modularml/mojo/issues/3653 ? The signature of __getitem__ for List makes it look like it returns a ref to itself though, which seemingly wouldn't need to allocate?
fn count_items(borrowed items: List[UInt8]) -> Int:
return len(items)


fn count_items_list(borrowed items: List[UInt8], offset: Int) -> Int:
return len(items) - offset


fn count_items_tensor(borrowed items: Tensor[DType.uint8], offset: Int) -> Int:
"""Mock function that would work on the tensor based on the offset instead of a slice
"""
return items.num_elements() - offset

fn main():
var item_list = List[UInt8]()
for i in range(10000):
item_list.append(i)

fn test_passing_slice() raises:
var sum = 0
for i in range(10000):
sum += count_items(item_list[i:])

fn test_passing_list() raises:
var sum = 0
for i in range(10000):
sum += count_items_list(item_list, i)

var item_tensor = Tensor(item_list)

fn test_passing_tensor() raises:
var sum = 0
for i in range(10000):
sum += count_items_tensor(item_tensor, i)
# Custom benchmark code that needs to be changed to the stdlib benchmark code
fn count_items(borrowed items: List[UInt8]) -> Int:
return len(items)


fn count_items_list(borrowed items: List[UInt8], offset: Int) -> Int:
return len(items) - offset


fn count_items_tensor(borrowed items: Tensor[DType.uint8], offset: Int) -> Int:
"""Mock function that would work on the tensor based on the offset instead of a slice
"""
return items.num_elements() - offset

fn main():
var item_list = List[UInt8]()
for i in range(10000):
item_list.append(i)

fn test_passing_slice() raises:
var sum = 0
for i in range(10000):
sum += count_items(item_list[i:])

fn test_passing_list() raises:
var sum = 0
for i in range(10000):
sum += count_items_list(item_list, i)

var item_tensor = Tensor(item_list)

fn test_passing_tensor() raises:
var sum = 0
for i in range(10000):
sum += count_items_tensor(item_tensor, i)
# Custom benchmark code that needs to be changed to the stdlib benchmark code
GitHub
[Feature Request] [stdlib] [proposal] Have all `fn getitem(self...
Review Mojo's priorities I have read the roadmap and priorities and I believe this request falls within the priorities. What is your request? Once we have Iterators designed or right now using ...
5 Replies
duck_tape
duck_tapeOP2w ago
GitHub
mojo/stdlib/src/collections/list.mojo at 61a97c6f995ed8a471d9613b90...
The Mojo Programming Language. Contribute to modularml/mojo development by creating an account on GitHub.
ModularBot
ModularBot2w ago
Congrats @duck_tape, you just advanced to level 2!
duck_tape
duck_tapeOP2w ago
I'm still wrong, that __getitem__ for the Reference to a list is for the scalar access. There does not appear to be a way to get a slice of a list without allocating.
duck_tape
duck_tapeOP2w ago
Answer: Create a Span from the list: Span(items) and then slice into that. But the function you pass to seems to also need to specify a Span as the argument otherwise it is maybe coercing back to a list and being slow. https://docs.modular.com/mojo/stdlib/utils/span/Span/#__init__
Span | Modular Docs
A non owning view of contiguous data.
Robert
Robert2w ago
I’ve been running into blerps as well iterating, parsing, and slicing thru lists. How do the constructors work with lists?
Want results from more Discord servers?
Add your server