ruPRIM Benutzerhandbuch
English | 简体中文 | 日本語 | Deutsch | Русский
Computerbibliotheken · Tensor-Framework · 中文
ruPRIM bietet Gerätetensorreduzierungen, kumulative Scans, elementweise Operationen und Indizierung. Diese Seite verwendet RudaTensor<R>, wobei R eine Gerätelaufzeit ist.
1. Abhängigkeiten konfigurieren
Das Cargo-Paket ist ruPRIM; Sein Rust-Importname ist ruprim. Aktivieren Sie Funktionen für die von Ihnen benötigten Vorgänge:
| Feature | Schnittstelle |
|---|---|
tensor-reduce |
ruprim::reduce::tensor: Ganztensor- und Achsenreduktionen |
tensor-reduce-autotune |
Autotuning für Reduktionen; aktiviert auch tensor-reduce |
tensor-scan |
ruprim::scan: kumulative Summe, Produkt, Minimum, Maximum |
elementwise |
ruprim::elementwise: elementweise Berechnung |
indexing |
ruprim::indexing: Auswahl, Slicing, Gather, Scatter; aktiviert auch elementwise |
Diese Konfiguration platziert das Anwendungsverzeichnis neben dem RUDA-Quellverzeichnis. Informationen zum NVIDIA-Setup finden Sie unter Erste Schritte.
[dependencies]
ruprim = { package = "ruPRIM", path = "../RUDA/ruPRIM", default-features = false, features = ["std", "tensor-reduce", "tensor-scan", "indexing"] }
ruda-core = { path = "../RUDA/ruda-core", default-features = false, features = ["std", "tensor-host-data"] }
ruda-kernel = { path = "../RUDA/ruda-kernel", default-features = false, features = ["frontend-std", "device-tensor"] }
ruda-driver-cuda = { path = "../RUDA/ruda-driver-cuda", default-features = false, features = ["std"] }
2. Summe, Zeilenreduzierungen und Scans
Dieser vollständige src/main.rs verwendet die F32-Matrix [[1, 2, 3], [4, 5, 6]], um seine Gesamtsumme, Zeilenmittelwerte, Zeilen-Argmax-Indizes und Zeilenpräfixsummen zu berechnen. Führen Sie cargo run aus dem Anwendungsverzeichnis aus:
use ruda_core::tensor::{DType, TensorMetadata, data::TensorData};
use ruda_driver_cuda::{CudaDevice, CudaRuntime};
use ruda_kernel::tensor::{readback::into_data_sync, transfer::from_data};
use ruprim::reduce::{
components::instructions::ReduceOperationConfig,
tensor::{KernelReduceStrategy, SumStrategy, reduce_dim, sum},
};
use ruprim::scan::cumsum;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let device = CudaDevice::default();
let input = from_data::<CudaRuntime>(
TensorData::new(vec![1f32, 2., 3., 4., 5., 6.], [2, 3]),
&device,
);
let total = sum(
input.clone(),
SumStrategy::Chained(KernelReduceStrategy::Unspecified),
)?;
assert_eq!(total.shape().as_slice(), &[1]);
assert_eq!(into_data_sync(total).to_vec::<f32>()?, [21.0]);
let means = reduce_dim(
input.clone(), None, 1,
KernelReduceStrategy::Unspecified, ReduceOperationConfig::Mean,
)?;
assert_eq!(means.shape().as_slice(), &[2, 1]);
assert_eq!(into_data_sync(means).to_vec::<f32>()?, [2.0, 5.0]);
let indices = reduce_dim(
input.clone(), Some(DType::I32), 1,
KernelReduceStrategy::Unspecified, ReduceOperationConfig::ArgMax,
)?;
assert_eq!(into_data_sync(indices).to_vec::<i32>()?, [2, 2]);
let prefix = cumsum(input, 1);
assert_eq!(into_data_sync(prefix).to_vec::<f32>()?, [1., 3., 6., 4., 9., 15.]);
Ok(())
}
sum reduziert alle Elemente und gibt die Form [1] zurück. reduce_dim verarbeitet nur die ausgewählte Achse, behält normalerweise den Rang bei und setzt die Achsenlänge auf 1. Zeile bedeutet daher, dass sie die Form [2, 1] und nicht [2] hat.
3. Reduktionsoperationen und -strategien
Vollständige Parameterreihenfolge für Tensorschnittstellen:
| Funktion | Zweck |
|---|---|
sum(tensor, strategy) |
Ganze Tensorsumme |
sum_fallback(tensor, strategy) |
Gesamttensorsumme; schaltet OneShot auf „Chained“ um, wenn die erforderliche atomare Addition nicht verfügbar ist |
reduce(tensor, output_dtype, strategy, config) |
Reduziert nacheinander jede Achse und gibt die Form zurück [1] |
reduce_dim(tensor, output_dtype, dim, strategy, config) |
Reduziert eine ausgewählte Achse |
Wählen Sie config mit ReduceOperationConfig aus:
| Vorgang | reduce_dim-Ausgabe |
|---|---|
Sum, Prod, Mean |
Summe, Produkt, Mittelwert; gewählte Achslänge 1 |
Min, Max, MaxAbs |
Minimaler, maximaler, maximaler Absolutwert; gewählte Achslänge 1 |
ArgMin, ArgMax |
Nullbasierte Indizes innerhalb der ausgewählten Achse; Achslänge 1 |
TopK(k) |
Top-k-Werte entlang der ausgewählten Achse; Achslänge k |
ArgTopK(k) |
Entsprechende Indizes innerhalb der Achse; Achslänge k |
Indexreduzierungen erfordern eine explizite Ganzzahlausgabe dtype wie Some(DType::I32). Für Wertminderungen übergeben Sie None; Der Ausgang behält den Eingang dtype. Verwenden Sie output_dtype nicht als allgemeinen Umwandlungsparameter. F16/BF16 Summe, Prod und Mittelwert verwenden die FP32-Akkumulation in diesem Pfad, bevor die Eingabe dtype geschrieben wird. Die Achse muss gültig sein und TopK k sollte in 1..=axis length sein. Verwenden Sie reduce_dim für TopK, nicht den Ganztensor reduce, der die endgültige Form auf [1] zurücksetzt.
Wählen Sie SumStrategy wie folgt:
OneShot(ruda_count): Legt explizit eine positive Arbeitsgruppenanzahl fest und erfordert eine atomare Addition für die Eingabe dtype.Chained(KernelReduceStrategy::Unspecified): verwendet abgestufte Reduktionen, ohne dass die globale atomare Addition der Gesamttensorsumme erforderlich ist; im Beispiel verwendet.Autotune: verfügbar mittensor-reduce-autotune. Ohne diese Funktion ist der StandardwertOneShot(4); Damit ist der StandardwertAutotune.
KernelReduceStrategy bietet Unspecified, Specific(ReduceStrategy) und funktionsgesteuertes Autotune. Verwenden Sie „Spezifisch“, um eine Low-Level-Strategie festzulegen. Ohne Autotuning ist die Standardeinstellung Nicht angegeben.
Reduktionsschnittstellen geben Result<RudaTensor<R>, ReduceError> zurück. Eine Achse außerhalb des gültigen Bereichs ergibt InvalidAxis; fehlende atomare Addition für OneShot ergibt MissingAtomicAdd. sum_fallback ersetzt nur den Fall der nicht unterstützten atomaren OneShot-Addition und wechselt nicht zur CPU-Ausführung.
4. Kumulative Scans
Alle vier Funktionen benötigen (tensor, dim):
| Funktion | Ergebnis für eine Eingabezeile [3, 1, 2] |
|---|---|
cumsum |
[3, 4, 6] |
cumprod |
[3, 3, 6] |
cummin |
[3, 1, 1] |
cummax |
[3, 3, 3] |
Dies sind inklusive Präfixoperationen. Ausgabeform und dtype stimmen mit der Eingabe überein; Stapelzeilen werden unabhängig voneinander verarbeitet. dim ist nullbasiert und muss innerhalb des Eingaberangs liegen. Scans geben Tensoren direkt zurück, nicht Result.
Der aktuelle Algorithmus liest das entsprechende Präfix für jede Ausgabeposition und ergibt O(n²) Gesamtlesevorgänge für eine Achse der Länge n. Berücksichtigen Sie diese Kosten für lange Scanachsen, anstatt die Arbeit allein anhand der Anzahl der Eingabeelemente abzuschätzen.
5. Auswahl, Sammeln, Schneiden und ganzzahlige Potenzen
Fügen Sie diese Funktion derselben Datei hinzu und rufen Sie indexing_example(&device)?; auf, bevor main zurückkehrt. Es verwendet die früheren Importe wieder:
fn indexing_example(device: &CudaDevice) -> Result<(), Box<dyn std::error::Error>> {
use ruda_core::tensor::element::Scalar;
use ruprim::{
elementwise::binary::integer_power,
indexing::{gather, select, slice},
};
let input = from_data::<CudaRuntime>(
TensorData::new(vec![1f32, 2., 3., 4., 5., 6.], [2, 3]),
device,
);
let columns = from_data::<CudaRuntime>(
TensorData::new(vec![2i32, 0], [2]), device,
);
let selected = select(input.clone(), 1, columns);
assert_eq!(into_data_sync(selected).to_vec::<f32>()?, [3., 1., 6., 4.]);
let positions = from_data::<CudaRuntime>(
TensorData::new(vec![2i32, 0, 1, 1], [2, 2]), device,
);
let gathered = gather(1, input.clone(), positions);
assert_eq!(into_data_sync(gathered).to_vec::<f32>()?, [3., 1., 5., 5.]);
let sliced = slice(input.clone(), &[0..2, 1..3]);
assert_eq!(into_data_sync(sliced).to_vec::<f32>()?, [2., 3., 5., 6.]);
let squared = integer_power::scalar(input, Scalar::Int(2));
assert_eq!(into_data_sync(squared).to_vec::<f32>()?, [1., 4., 9., 16., 25., 36.]);
Ok(())
}
| Funktion | Indizes und Ausgabe |
|---|---|
select(tensor, dim, indices) |
Eindimensionale Ganzzahlindizes; Wendet die gleichen Positionen auf andere Dimensionen an und ersetzt die Länge der ausgewählten Achse durch die Indexanzahl |
gather(dim, tensor, indices) |
dim ist das erste Argument; Wählt einen Eingabewert pro Ausgabeposition, wobei die Ausgabeform der Form der Indizes entspricht |
slice(tensor, ranges) |
Halboffene start..end-Bereiche pro Achse; Im Beispiel werden die Spalten 1 und 2 aus allen Zeilen ausgewählt |
slice_assign(tensor, slices, value) |
Verwendet ruda_core::tensor::Slice; schreibt einen Wert in den ausgewählten Bereich und gibt den aktualisierten Tensor zurück |
Indizes und Daten müssen sich ein Gerät teilen; Indizes müssen gültige, auf Null basierende Ganzzahlen sein. Verwenden Sie zum Sammeln Indextensoren mit demselben Rang und passenden nicht ausgewählten Dimensionen. Jede Beispielzeile kann verschiedene Spalten auswählen. Die Slice-Bereiche müssen innerhalb ihrer Achsen liegen und die Form des Zuweisungswerts muss mit der ausgewählten Region übereinstimmen.
Ganzzahlige Potenzen sind in ruprim::elementwise::binary::integer_power:
scalar(input, Scalar::Int(exponent)): Verwendet einen ganzzahligen Exponenten für alle Elemente.tensor(base, exponents): verwendet Gleitkommabasen und ganzzahlige Exponententensoren mit Broadcast-kompatiblen Formen; Ausgang behält Basis dtype.
Verwenden Sie Scalar::Int(-2) für einen negativen Exponenten, anstatt den Exponenten in einen Gleitkommawert umzuwandeln. Indizierung und elementweise Schnittstellen geben Gerätetensoren direkt zurück. Um eine Eingabe für andere Operationen beizubehalten, übergeben Sie geklonte Handles wie im Beispiel und behalten Sie den von jeder Operation zurückgegebenen Tensor bei.
API Referenz: Reduktionen, Scans, Indizierung, Ganzzahlpotenzen.