Skip to content

Request tensor factory methods accepting Span<T> #1326

Description

@dje-dev

Currently it is apparently required to provide an exactly sized array to the tensor static factory methods.

In the case where (for example) tensors are being created with variable batch sizes, the size of the input will vary. With the current design, it is necessary to allocate a new array appropriate for each batch (and one cannot use a pooled object which may be oversized).

Instead, callers may prefer to allocate a large fixed managed array and then only pass the appropriately sized Span over this array to the factor method, thereby saving an object allocation on every call.

Example given below of the suggested API enhancement (to be replicated across the various data types)
EXISTING: public static Tensor tensor(short[] rawArray, ...
DESIRED: public static Tensor tensor(ReadOnlySpan<Span> rawSpan, ...

Activity

  1. NiklasGustafsson commented on Jun 11, 2024

    @NiklasGustafsson

    In order to create a tensor from the span, we would have to be able to pin it in memory, and GCHandle.Alloc() won't accept a Span, as far as can tell. We can add the overloads as a convenience, but I believe the allocation (and copy) is going to have to happen.

  2. dje-dev commented on Jun 12, 2024

    @dje-dev
    Author

    Good point, Span doesn't quite work.

    Instead one or both of these approaches could seemingly avoid one full memory copy of the full tensor to be loaded onto GPU:

    1. Keep current signature but relax to accept oversized T[] relative to the provided shape, instead of throwing System.ArgumentException: 'mismatched total size creating a tensor from an array'
    2. Follow the ONNX pattern of supporting this over Memory (which has a Pin method):
      • public static OrtValue CreateTensorValueFromMemory(OrtMemoryInfo memoryInfo, Memory memory, long[] shape) where T : unmanaged
  3. NiklasGustafsson commented on Jun 12, 2024

    @NiklasGustafsson

    We can easily accommodate the first method by just relaxing the constraints. Memory can be pinned, but I don't know how to get a pointer to the underlying array without copying, which is needed in order to create a native tensor, so that requires more thought.

  4. NiklasGustafsson commented on Jun 12, 2024

    @NiklasGustafsson

    Addressed in PR #1329

  5. yueyinqiu commented on Jun 12, 2024

    @yueyinqiu
    Contributor

    @NiklasGustafsson

    I would suggest an extra option to control this behavior, instead of cutting it implicitly.

    Meanwhile just cutting the "tail" may be far from expectation I think. To let it make sense, at least an option for start indices is required (since the issue is talking about "spans").

    I know it's maybe... impossible currently. So actually I suggest to leave this issue "not planned"/"not current" and revert the changes.

  6. NiklasGustafsson commented on Jun 12, 2024

    @NiklasGustafsson

    Hmmm... I really liked the idea of allowing the underlying buffer to be bigger than needed. Low cost, very low risk.

    To handle the starting offset > 0, you could just slice it without any copying: t[offset..] after you create it. It allocates a new managed Tensor, but does not copy the buffer.

  7. yueyinqiu commented on Jun 12, 2024

    @yueyinqiu
    Contributor

    Ahhh... I got that. It do solve the original case of this issue and could be useful in some other similar situations.

  8. NiklasGustafsson commented on Jun 13, 2024

    @NiklasGustafsson

    I figured out how to get the address out of a Memory<T>. Should have read the docs better... :-) I'm reopening and will add some Memory overloads. Probably not as many different variants as for T[]

  9. NiklasGustafsson commented on Jun 13, 2024

    @NiklasGustafsson

    Okay, so both of @dje-dev suggestions have been implemented and merged. Thanks for the ideas!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions