Основная цель - реализовать граф вычислений, который обрабатывает узлы со значениями и узлы с операторами (вспомним простые арифметические c операторы, такие как сложение, вычитание, умножение и т. Д. c ..). Узел оператора может принимать до двух узлов значений и «производить» результирующий узел значений.
До сих пор я использовал перечисление для различения значения и узла оператора:
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>
}
Обновление : Node::Value
содержит структуру, которая сама содержит ссылку на объект признака ValueType
, который реализуется различными конкретными типами.
Но тут возникает проблема. Во время компиляции обобщенные типы c будут исключены и заменены фактическими типами. Общий c тип T
также распространяется по всему графу вычислений (очевидно):
pub struct ComputationGraph<T> where T : Copy + Clone {
nodes: Vec<Node<T>>
}
Это фактически ограничивает использование ComputeGraph
одним указанным c ValueType
.
Кроме того, тип generi c T
не может быть Sized
, поскольку узел значения может иметь тип opqaue, обрабатываемый другим бэкэндом, недоступным через rust (представьте себе, что типы C opqaue доступны через FFI).
Одним из возможных решений этой проблемы было бы введение дополнительного типа enum, который «отражает» конкретную реализацию признака типа оценки, упомянутого выше. этот подход был бы подобен тому, что делает enum dispatch .
Есть ли что-нибудь, о чем я не думал использовать несколько реализаций ValueType
?
обновление :
Я хочу получить следующий код:
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);
}
обновление
ValueType<T>
выглядит следующим образом
pub trait ValueType<T> {
fn get_size(&self) -> usize;
fn get_value(&self) -> T;
}
update
Чтобы еще больше повысить ясность моего вопроса, подумайте о небольшой библиотеке BLAS, поддерживаемой OpenCL. Управление памятью и взаимодействие с устройством должны быть прозрачными для пользователя. Тип Matrix выделяет пространство на устройстве OpenCL с типами в качестве буфера примитивного типа, и соответствующий вызов возвратит указатель на эту указанную c область памяти. Представьте себе операцию, которая масштабирует матрицу по скалярному типу, который представлен примитивным значением. Буфер (указатель на) и скаляр могут быть переданы в функцию ядра. Возвращаясь к ComputeGraph
, кажется очевидным, что все операции BLAS образуют некоторый тип вычислительного графа, который может быть сведен к линейному списку инструкций (подумайте здесь об установке аргументов ядра, выделении буферов, постановке в очередь ядра, сохранении результат, et c ...). Сказав все это, граф вычислений должен уметь хранить узлы значений с различными типами.