简体   繁体   English

如何有效地处理 rust 中不同具体类型的泛型类型?

[英]How to handle generic types with different concrete types in rust efficiently?

The main goal is to implement a computation graph, that handles nodes with values and nodes with operators (think of simple arithmetic operators like add, subtract, multiply etc..).主要目标是实现一个计算图,它处理带有值的节点和带有运算符的节点(想想简单的算术运算符,如加法、减法、乘法等)。 An operator node can take up to two value nodes, and "produces" a resulting value node.一个算子节点最多可以占用两个值节点,并“产生”一个结果值节点。

Up to now, I'm using an enum to differentiate between a value and operator node:到目前为止,我使用枚举来区分值和操作符节点:

pub enum Node<'a, T> where T : Copy + Clone {
    Value(ValueNode<'a, T>),
    Operator(OperatorNode)
}

pub struct ValueNode<'a, T> {
   id: usize, 
   value_object : &'a dyn ValueType<T>
}

Update : Node::Value contains a struct, which itself contains a reference to a trait object ValueType , which is being implemented by a variety of concrete types.更新Node::Value包含一个结构,它本身包含对特征 object ValueType的引用,该特征由各种具体类型实现。

But here comes the problem.但问题来了。 During compililation, the generic types will be elided, and replaced by the actual types.在编译期间,泛型类型将被省略,并由实际类型替换。 The generic type T is also being propagated throughout the computation graph (obviously):泛型T也在整个计算图中传播(显然):

pub struct ComputationGraph<T> where T : Copy + Clone {
    nodes: Vec<Node<T>>
}

This actually restricts the usage of ComputeGraph to one specific ValueType .这实际上将ComputeGraph的使用限制为一个特定的ValueType

Furthermore the generic type T cannot be Sized , since a value node can be an opqaue type handled by a different backend not available through rust (think of C opqaue types made available through FFI).此外,通用类型T不能是Sized ,因为值节点可以是由 rust 不可用的不同后端处理的 opqaue 类型(想想通过 FFI 提供的 C opqaue 类型)。

One possible solution to this problem would be to introduce an additional enum type, that "mirrors" the concrete implementation of the valuetype trait mentioned above.这个问题的一个可能的解决方案是引入一个额外的枚举类型,它“镜像”上面提到的 valuetype 特征的具体实现。 this approach would be similiar, that enum dispatch does.这种方法与枚举调度类似。

Is there anything I haven't thought of to use multiple implementations of ValueType ?有什么我没有想到要使用ValueType的多个实现的吗?

update :更新

What i want to achive is following code:我想要实现的是以下代码:

pub struct Scalar<T> where T : Copy + Clone{
    data : T
}

fn main() {
   let cg = ComputeGraph::new();

   // a new scalar type. doesn't have to be a tuple struct   
   let a = Scalar::new::<f32>(1.0);

   let b_size = 32; 
   let b = Container::new::<opaque_type>(32);

   let op = OperatorAdd::new();

   // cg.insert_operator_node constructs four nodes: 3 value nodes  
   // and one operator nodes internally. 
   let result = cg.insert_operator_node::<Container>(&op, &a, &b);

} 

update更新

ValueType<T> looks like this ValueType<T>看起来像这样

pub trait ValueType<T> {
    fn get_size(&self) -> usize;
    fn get_value(&self) -> T;
}

update更新

To further increase the clarity of my question think of a small BLAS library backed by OpenCL.为了进一步提高我的问题的清晰度,请考虑一个由 OpenCL 支持的小型 BLAS 库。 The memory management and device interaction shall be transparent to the user. memory 管理和设备交互对用户应该是透明的。 A Matrix type allocates space on an OpenCL device with types as a primitive type buffer, and the appropriate call will return a pointer to that specific region of memory. Matrix 类型在 OpenCL 设备上分配空间,类型为原始类型缓冲区,适当的调用将返回指向 memory 的特定区域的指针。 Think of an operation that will scale the matrix by a scalar type, that is being represented by a primitive value.考虑一个将通过标量类型缩放矩阵的操作,它由原始值表示。 Both the (pointer to the) buffer and the scalar can be passed to a kernel function. (指向)缓冲区和标量都可以传递给 kernel function。 Going back to the ComputeGraph , it seems obvious, that all BLAS operations form some type of computational graph, which can be reduced to a linear list of instructions ( think here of setting kernel arguments, allocating buffers, enqueue the kernel, storing the result, etc... ).回到ComputeGraph ,似乎很明显,所有 BLAS 操作都形成某种类型的计算图,可以简化为指令的线性列表(在这里考虑设置 kernel arguments,分配缓冲区,将 Z50484C19F1AFDAF38821AFDAF33 入队存储结果ETC... )。 Having said all that, a computation graph needs to be able to store value nodes with a variety of types.说了这么多,计算图需要能够存储各种类型的值节点。

As always the answer to the problem posed in the question is obvious.与往常一样,问题中提出的问题的答案是显而易见的。 The graph expects one generic type (with trait bounds).该图需要一种泛型类型(具有特征边界)。 Using an enum to "cluster" various subtypes was the solution, as already sketched out in the question.正如问题中已经概述的那样,使用枚举来“聚集”各种子类型是解决方案。

An example to illustrate the solution.一个例子来说明解决方案。 Consider following "subtypes":考虑以下“子类型”:

struct Buffer<T> {
   // fields
}

struct Scalar<T> {
   // fields
}

struct Kernel {
   // fields
}

The value containing types can be packed into an enum:包含类型的值可以打包到一个枚举中:

enum MemType {
   Buffer(Buffer<f32>);
   Scalar(Scalar<f32>);
   // more enum variants ..
}

Now MemType and Kernel can now be packed in an enum as well现在MemTypeKernel现在也可以打包在一个枚举中

enum Node {
   Value(MemType);
   Operator(Kernel);
}

Node can now be used as the main type for nodes/vertices inside the graph. Node现在可以用作图中节点/顶点的主要类型。 The solution might not be very elegant, but it does the trick for now.该解决方案可能不是很优雅,但现在可以解决问题。 Maybe some code restructuring might be done in the future.也许将来可能会进行一些代码重组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM