O dialeto Shardy (SDY) define uma representação de fragmentação de tensor com base em eixo, além de outros componentes da API para anexar fragmentações aos tensores.
Operações
sdy.constant
(sdy::ConstantOp)
Operação constante
Produz um tensor output
de uma value
constante.
Consulte: https://github.com/openxla/stablehlo/blob/main/docs/spec.md#constant
Exemplo:
%output = sdy.constant dense<[[0.0, 1.0], [2.0, 3.0]]> : tensor<2x2xf32>
Características: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable
, InferTypeOpInterface
, NoMemoryEffect (MemoryEffectOpInterface)
Efeitos: MemoryEffects::Effect{}
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
value | ::mlir::ElementsAttr | atributo de vetor/tensor constante |
Resultados:
Resultado | Descrição |
---|---|
output |
tensor de valores de qualquer tipo |
sdy.data_flow_edge
(sdy::DataFlowEdgeOp)
Operação de borda do fluxo de dados.
Sintaxe:
operation ::= `sdy.data_flow_edge` $input (`sharding````=``` $sharding^)? attr-dict `:` type($result)
Uma borda de fluxo de dados de alguma operação X define uma ponte entre um conjunto de origens (cada uma é um operando de X ou um operando do terminador de bloco de X) e um conjunto de destinos (cada um é um resultado de X ou um argumento de bloco de X), de modo que todas as origens e destinos precisam ser particionados da mesma maneira.
Uma operação pode ter várias arestas de fluxo de dados ortogonais entre si.
Exemplo:
y_0, ..., y_n = while (x_0, ..., x_n)
((pred_arg_0,... , pred_arg_n) { ... })
((body_arg_0,..., body_arg_n) {
...
return return_value_0, ..., return_value_n
})
Essa operação while tem n bordas de fluxo de dados, e a i-ésima borda de fluxo de dados está entre
as fontes x_i
, return_value_i
e os destinos y_i
, pred_arg_i
e
body_arg_i
.
Um sdy.data_flow_edge
usa como entrada o destino raiz de uma aresta (pode ser
qualquer um dos destinos, mas de preferência um resultado de operação em vez de um argumento
de bloco), que não pode ter outros usos. Essa operação não é pura porque pode receber uma entrada que originalmente não tinha nenhum uso.
O sdy.data_flow_edge
também mantém um fragmentação opcional para todos os destinos do
edge, e essa fragmentação precisa ser atualizada em vez da fragmentação dos
destinos (se puder ser anexada) durante a propagação. Isso é útil quando uma operação
tem muitas arestas, porque é muito mais eficiente:
- se propagam separadamente em cada borda.
- Atualize o sharding de cada borda separadamente em vez de todas as metas de uma vez.
Por exemplo, uma operação tem um único
TensorShardingPerValueAttr
imutável para shardings de resultados. - Adicione cada borda à lista de trabalhos separadamente quando o sharding de uma origem tiver mudado.
A propagação vai propagar os particionamentos entre todas as origens e destinos de um
sdy.data_flow_edge
como se fosse uma operação regular com as origens como operandos
e os destinos como resultados, além de uma identidade sdy.op_sharding_rule
. Isso significa
que a propagação para frente é de origens para destinos, e a propagação para trás
é de destinos para origens.
Não permitimos que a entrada de um sdy.data_flow_edge
seja definida por uma
operação SdyDialect
. Portanto, podemos presumir que ela é definida por uma operação com
atributo sdy.sharding
não registrado.
Características: SameOperandsAndResultType
Interfaces: InferTypeOpInterface
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
sharding | ::mlir::sdy::TensorShardingAttr | Fragmentação de tensor |
Operandos:
Operand | Descrição |
---|---|
input |
em forma de valores de qualquer tipo |
Resultados:
Resultado | Descrição |
---|---|
result |
com qualquer tipo de valor |
sdy.manual_computation
(sdy::ManualComputationOp)
Operação de paralelismo em vários dispositivos com coletivos manuais
Sintaxe:
operation ::= `sdy.manual_computation` `(`operands`)`
`in_shardings````=```custom<StrippedTensorShardingPerValueAttr>($in_shardings)
`out_shardings````=```custom<StrippedTensorShardingPerValueAttr>($out_shardings)
`manual_axes````=```$manual_axes
custom<SingleBlockRegionNoBlockId>($body)
attr-dict
`:`
functional-type(operands, results)
Vá para uma região escrita em termos de código local por dispositivo com coletivos explícitos, em que as formas lógicas correspondem às formas de buffer físico por dispositivo, e os coletivos correspondem exatamente à comunicação física entre dispositivos.
O corpo é local wrt os manuais_eixos. A propagação vai ocorrer pelo corpo em qualquer eixo livre, ou seja, aqueles que não estão na lista manual_axes.
Características: IsolatedFromAbove
, RecursiveMemoryEffects
, SingleBlockImplicitTerminator<ReturnOp>
, SingleBlock
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
in_shardings | ::mlir::sdy::TensorShardingPerValueAttr | Fragmentação de tensor por operando/resultado de uma operação |
out_shardings | ::mlir::sdy::TensorShardingPerValueAttr | Fragmentação de tensor por operando/resultado de uma operação |
manual_axes | ::mlir::sdy::ManualAxesAttr |
Operandos:
Operand | Descrição |
---|---|
tensors |
variadic de tensor classificado de qualquer tipo de valores |
Resultados:
Resultado | Descrição |
---|---|
results |
variadic de tensor classificado de qualquer tipo de valores |
sdy.mesh
(sdy::MeshOp)
Malha nomeada
Sintaxe:
operation ::= `sdy.mesh` $sym_name `=` $mesh attr-dict
Define uma nova malha com nome. Todas as malhas em um módulo precisam ter o mesmo número
de dispositivos, exceto as malhas com um único device_id.
A malha é uma operação Symbol
que aparece na SymbolTable
do módulo e pode ser referenciada pelo name
.
Características: HasParent<ModuleOp>
Interfaces: Symbol
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
sym_name | ::mlir::StringAttr | atributo de string |
mesh | ::mlir::sdy::MeshAttr | Malha de eixos e uma lista de dispositivos |
sdy.named_computation
(sdy::NamedComputationOp)
Operação de cálculo nomeada
Sintaxe:
operation ::= `sdy.named_computation` `<`$name`>` `` `(` $operands `)`
(`in_shardings````=```custom<StrippedTensorShardingPerValueAttr>($in_shardings)^)?
(`out_shardings````=```custom<StrippedTensorShardingPerValueAttr>($out_shardings)^)?
custom<SingleBlockRegionNoBlockId>($body)
attr-dict
`:` functional-type($operands, results)
Agrupa uma computação, ou seja, um bloco de operações, e dá um nome a ela. A propagação vai fluir para dentro/fora da região como se tudo estivesse inline.
Isso pode ser usado para processar a propagação por instruções de chamada para outras
funções. Todos os usuários do Shardy precisam escrever um cartão de importação/exportação que
converta as operações de chamada em operações sdy.named_computation
, duplicando/copiando
o corpo da função chamada para o corpo do named_computation
.
O tipo de cada argumento de bloco e os valores retornados na região precisam ser idênticos ao tipo dos operandos e ao tipo de resultados da operação.
Exemplo:
%1 = sdy.named_computation<"foo">(%0) (%arg1: tensor<16x32xf32>) {
sdy.return %arg1 : tensor<16x32xf32>
} : (tensor<16x32xf32>) -> tensor<16x32xf32>
Características: IsolatedFromAbove
, RecursiveMemoryEffects
, RecursivelySpeculatableImplTrait
, SingleBlockImplicitTerminator<ReturnOp>
, SingleBlock
Interfaces: ConditionallySpeculatable
, ShardableDataFlowOpInterface
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
name | ::mlir::StringAttr | atributo de string |
in_shardings | ::mlir::sdy::TensorShardingPerValueAttr | Fragmentação de tensor por operando/resultado de uma operação |
out_shardings | ::mlir::sdy::TensorShardingPerValueAttr | Fragmentação de tensor por operando/resultado de uma operação |
Operandos:
Operand | Descrição |
---|---|
operands |
variadic de qualquer tipo |
Resultados:
Resultado | Descrição |
---|---|
«sem nome» | variadic de qualquer tipo |
sdy.propagation_barrier
(sdy::PropagationBarrierOp)
Operação de barreira de propagação
Sintaxe:
operation ::= `sdy.propagation_barrier` $input `allowed_direction````=```$allowed_direction attr-dict `:` type($input)
Essa operação funciona como uma operação de identidade, gerando o mesmo valor que recebeu como entrada. Mas, em termos de propagação, isso só permite que a propagação flua por ele em uma determinada direção.
Isso impede que os fragmentos sejam propagados entre os usos do resultado da operação de barreira e do operando.
FORWARD
significa que os fragmentos só podem fluir do operando para o resultado.BACKWARD
significa que as fragmentações só podem fluir do resultado para o operando.NONE
significa que nenhum sharding pode ser propagado por essa operação.- Não é possível especificar
BOTH
, porque essa operação seria redundante.
Características: AlwaysSpeculatableImplTrait
, Elementwise
, SameOperandsAndResultType
Interfaces: ConditionallySpeculatable
, InferTypeOpInterface
, NoMemoryEffect (MemoryEffectOpInterface)
Efeitos: MemoryEffects::Effect{}
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
allowed_direction | ::mlir::sdy::PropagationDirectionAttr | Enumeração de direção de propagação |
Operandos:
Operand | Descrição |
---|---|
input |
tensor classificado de qualquer tipo de valores |
Resultados:
Resultado | Descrição |
---|---|
result |
tensor classificado de qualquer tipo de valores |
sdy.reshard
(sdy::ReshardOp)
Refragmenta um tensor para uma fragmentação diferente
Sintaxe:
operation ::= `sdy.reshard` $input $sharding attr-dict `:` type($result)
Refaz o sharding do tensor de entrada com o sharding especificado, que é diferente do sharding atual do tensor de entrada.
Tanto ShardingConstraintOp quanto ReshardOp anexam um particionamento a um tensor. O ciclo de vida deles é:
- Antes da propagação de fragmentação, o ShardingConstraintOp é adicionado pelos usuários.
- A propagação da fragmentação consome ShardingConstraintOp. Não há ShardingConstraintOp nos resultados da propagação de fragmentação. Em vez disso, ReshardOp pode ser adicionado, se necessário.
- Um particionador converte uma ReshardOp em uma operação coletiva (ou uma operação de identidade). Não deve haver ReshardOp nos resultados do particionador.
// TODO(b/331680067). Adicione um padrão de canonização para remover operações // de refragmentação redundantes.
Características: AlwaysSpeculatableImplTrait
, Elementwise
, SameOperandsAndResultType
Interfaces: ConditionallySpeculatable
, InferTypeOpInterface
, NoMemoryEffect (MemoryEffectOpInterface)
Efeitos: MemoryEffects::Effect{}
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
sharding | ::mlir::sdy::TensorShardingAttr | Fragmentação de tensor |
Operandos:
Operand | Descrição |
---|---|
input |
tensor de valores de qualquer tipo |
Resultados:
Resultado | Descrição |
---|---|
result |
tensores de qualquer tipo |
sdy.return
(sdy::ReturnOp)
A operação sdy.return
encerra as regiões anexadas às operações baseadas em região sdy
e a qualquer outra operação baseada em região Shardy. Ele é variado: usa como argumentos uma lista de valores com tipos que podem ser qualquer um (mas do mesmo tipo, por exemplo, AnyTensor
) e, portanto, podem ser reutilizados em vários níveis da pilha de IR fragmentada.
Sintaxe:
operation ::= `sdy.return` attr-dict ($results^ `:` type($results))?
Características: AlwaysSpeculatableImplTrait
, Terminator
Interfaces: ConditionallySpeculatable
, NoMemoryEffect (MemoryEffectOpInterface)
Efeitos: MemoryEffects::Effect{}
Operandos:
Operand | Descrição |
---|---|
results |
variadic de qualquer tipo |
sdy.sharding_constraint
(sdy::ShardingConstraintOp)
Restrição de um tensor ao particionamento especificado
Sintaxe:
operation ::= `sdy.sharding_constraint` $input $sharding attr-dict `:` type($result)
Anexa um sharding a um tensor intermediário (por exemplo, o resultado de uma matmul) para indicar como esse tensor ou um subconjunto de usos dele precisa ser fragmentado.
Se o particionamento tiver dimensões abertas e eixos sem restrição, isso significa que o tensor pode ser particionado de acordo com as dimensões abertas.
Essa operação pode:
- Não tem uso (solto), o que significa que o particionamento anexado é como o tensor de entrada precisa ser particionado.
- Ter usos: significa que o particionamento anexado é como os usos da operação de restrição de particionamento precisam ser particionados, enquanto outros usos do tensor de entrada podem ter um particionamento diferente. Se o tensor de entrada não tiver outros usos, o comportamento será o mesmo do caso sem usos.
Características: Elementwise
, SameOperandsAndResultType
Interfaces: InferTypeOpInterface
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
sharding | ::mlir::sdy::TensorShardingAttr | Fragmentação de tensor |
Operandos:
Operand | Descrição |
---|---|
input |
tensor de valores de qualquer tipo |
Resultados:
Resultado | Descrição |
---|---|
result |
tensor de valores de qualquer tipo |
sdy.sharding_group
(sdy::ShardingGroupOp)
Operação de fragmentação de grupo
Sintaxe:
operation ::= `sdy.sharding_group` $input `group_id````=```$group_id attr-dict `:` type($input)
Essa operação fornece uma interface para atribuir tensores a grupos de fragmentação ( grupos de tensores que serão forçados a ter fragmentações idênticas). Durante a propagação, assim que um elemento de grupo é dividido, todos os outros membros são divididos da mesma forma. Essa operação usa o ID do grupo de argumentos e não retorna nenhum resultado, mas modifica a representação do grupo de fragmentação interna para adicionar o tensor de entrada ao grupo com o ID fornecido.
Atributos:
Atributo | Tipo de MLIR | Descrição |
---|---|---|
group_id | ::mlir::IntegerAttr | Atributo de número inteiro sem sinal de 64 bits |
Operandos:
Operand | Descrição |
---|---|
input |
tensor classificado de qualquer tipo de valores |
Atributos
AxisRefAttr
Referência a um eixo completo ou a um subeixo dividido
Sintaxe:
#sdy.axis_ref<
::llvm::StringRef, # name
SubAxisInfoAttr # sub_axis_info
>
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
nome | ::llvm::StringRef |
nome |
sub_axis_info | SubAxisInfoAttr |
DimMappingAttr
Lista de índices de fatores para uma dimensão
Todos os índices de fatores precisam estar no intervalo [0, num_factors], e uma lista vazia
indica que este é um mapeamento nulo (analisado/impresso com *
),
ou seja, a dimensão não é mapeada para nenhum fator.
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
factor_indices | ::llvm::ArrayRef<int64_t> |
DimensionShardingAttr
Fragmentação de dimensão
Lista de nomes de eixos para fragmentar uma dimensão de tensor do maior para o menor, um booleano que indica se a dimensão pode ser ainda mais fragmentada e um número inteiro opcional que indica a prioridade da fragmentação da dimensão, que será respeitada durante a propagação da fragmentação. As prioridades são originadas de anotações de fragmentação do usuário, e um valor menor indica uma prioridade mais alta. A prioridade mais alta é assumida quando está faltando a prioridade na anotação.
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
eixos | ::llvm::ArrayRef<AxisRefAttr> |
lista de referências de eixos |
is_closed | bool |
|
prioridade | std::optional<int64_t> |
ManualAxesAttr
Sintaxe:
#sdy.manual_axes<
::llvm::ArrayRef<StringAttr> # value
>
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
valor | ::llvm::ArrayRef<StringAttr> |
MeshAttr
Malha de eixos e uma lista de dispositivos
Sintaxe:
#sdy.mesh<
::llvm::ArrayRef<MeshAxisAttr>, # axes
::llvm::ArrayRef<int64_t> # device_ids
>
Uma malha é uma lista de eixos e uma lista opcional de IDs de dispositivos que especificam a ordem dos dispositivos.
Se a lista de eixos estiver vazia, a malha terá um eixo implícito sem nome de tamanho 1. Nesse caso, se uma lista de IDs de dispositivo não for fornecida, a lista implícita de IDs de dispositivos será [0]. Se uma lista de IDs de dispositivos for fornecida, ela precisará conter um único número inteiro de qualquer valor não negativo. Chamamos isso de caso de fragmentação máxima.
Para todos os casos de fragmentação não máxima, se uma lista de IDs de dispositivos for especificada, o produto dos tamanhos de eixo precisa corresponder ao número de dispositivos. Se uma lista de IDs de dispositivo não for especificada, a lista implícita de IDs de dispositivo será iota(product(axes)). Para simplificar, também não é permitido especificar uma lista de IDs de dispositivo igual a iota(product(axes)); nesse caso, uma lista de IDs de dispositivo não pode ser especificada.
Confira alguns exemplos de malhas:
- Uma malha vazia representa uma malha de marcador de posição que pode ser substituída durante a propagação: <[]>
- Uma malha com um eixo sem nome e um ID de dispositivo explícito, que normalmente é usado para representar o máximo de fragmentação: <[], device_ids=[3]>
- Uma malha com dois eixos e IDs de dispositivo implícitos iota(6): <["a"=2, "b"=3]>
- Uma malha com dois eixos e IDs de dispositivo explícitos que especificam a ordem do dispositivo: <["a"=3, "b"=2], device_ids=[0, 2, 4, 1, 3, 5]>
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
eixos | ::llvm::ArrayRef<MeshAxisAttr> |
|
device_ids | ::llvm::ArrayRef<int64_t> |
MeshAxisAttr
Eixo nomeado em uma malha
Sintaxe:
#sdy.mesh_axis<
::llvm::StringRef, # name
int64_t # size
>
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
nome | ::llvm::StringRef |
nome |
tamanho | int64_t |
OpShardingRuleAttr
Especifica como uma operação pode ser particionada.
Sintaxe:
#sdy.op_sharding_rule<
::llvm::ArrayRef<int64_t>, # factor_sizes
::llvm::ArrayRef<TensorMappingAttr>, # operand_mappings
::llvm::ArrayRef<TensorMappingAttr>, # result_mappings
bool # is_custom_rule
>
Uma regra de fragmentação especifica como uma operação pode ser particionada de acordo com várias propriedades na operação, como qualquer atributo, a forma dos operandos, a forma dos resultados etc. Por exemplo:
%0 = stablehlo.add %arg0, %arg1 {
sdy.sharding_rule = #sdy.op_sharding_rule<
([i, j],[i, j])->([i, j])
{i=8, j=8}>
} : tensor<8x8xf32>
%1 = stablehlo.dot_general %arg2, %arg3, contracting_dims = [1] x [0] {
sdy.sharding_rule = #sdy.op_sharding_rule<
([i, k],[k, j])->([i, j])
{i=8, j=16, k=8}>
}: (tensor<8x8xf32>, tensor<8x16xf32>) -> tensor<8x16xf32>
Permitimos fatores com tamanho 1, mesmo que não possam ser divididos, principalmente para completar, já que muitas operações, como operações pontuais, têm dimensões de tamanho um que correspondem a operandos e resultados.
is_custom_rule
descreve se essa é uma regra definida por um usuário para uma
operação stablehlo.custom_call
. Como o particionador não sabe como particionar essas operações, o usuário precisa informar como fazer isso. Quando é uma regra personalizada, ela é sempre preservada/nunca removida. is_custom_rule
só pode ser verdadeiro para operações stablehlo.custom_call
.
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
factor_sizes | ::llvm::ArrayRef<int64_t> |
|
operand_mappings | ::llvm::ArrayRef<TensorMappingAttr> |
|
result_mappings | ::llvm::ArrayRef<TensorMappingAttr> |
|
is_custom_rule | bool |
SubAxisInfoAttr
Informações sobre como esse eixo secundário é derivado do eixo completo
Sintaxe:
#sdy.sub_axis_info<
int64_t, # pre_size
int64_t # size
>
Ao dividir um eixo completo em n subeixos, ele é remodelado em [k_1,...,k_n], e o i-ésimo subeixo pode ser expresso pelo produto de todos os tamanhos de eixo à esquerda de m=prod(k_1,...,k_(i-1))
(também conhecido como pré-tamanho) e tamanho k_i. Portanto, o atributo sub-axis-info contém esses dois números e é
indicado da seguinte maneira: (m)k
para o pré-tamanho m e o tamanho k.
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
pre_size | int64_t |
|
tamanho | int64_t |
TensorMappingAttr
Mapeamentos de fatores para cada dimensão de um tensor.
Sintaxe:
#sdy.tensor_mapping<
::llvm::ArrayRef<DimMappingAttr> # dim_mappings
>
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
dim_mappings | ::llvm::ArrayRef<DimMappingAttr> |
TensorShardingAttr
Fragmentação de tensor
Sintaxe:
#sdy.sharding<
::mlir::Attribute, # mesh_or_ref
::llvm::ArrayRef<DimensionShardingAttr>, # dim_shardings
::llvm::ArrayRef<AxisRefAttr> # replicated_axes
>
Um fragmentação de tensor é vinculado a uma malha específica e só pode referenciar nomes de eixos dessa malha. Os shardings de dimensão informam, para cada dimensão do tensor, ao longo de quais eixos (ou subeixos) ele é dividido de maior para menor. Todos os outros eixos que não fragmentam uma dimensão são replicados de forma implícita ou explícita (se aparecerem na lista de eixos replicados).
A malha a que esse fragmentação está vinculada pode ser especificada por um nome de símbolo,
fazendo referência a um símbolo MeshOp
correspondente ou a um MeshAttr
inline.
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
mesh_or_ref | ::mlir::Attribute |
mesh attr ou flat mesh symbol reference attr |
dim_shardings | ::llvm::ArrayRef<DimensionShardingAttr> |
|
replicated_axes | ::llvm::ArrayRef<AxisRefAttr> |
lista de referências de eixos |
TensorShardingPerValueAttr
Fragmentação de tensor por operando/resultado de uma operação
Sintaxe:
#sdy.sharding_per_value<
::llvm::ArrayRef<TensorShardingAttr> # shardings
>
Parâmetros:
Parâmetro | Tipo C++ | Descrição |
---|---|---|
fragmentações | ::llvm::ArrayRef<TensorShardingAttr> |
Enums
PropagationDirection
Enumeração de direção de propagação
Casos:
Símbolo | Valor | String |
---|---|---|
NENHUMA | 0 |
NENHUMA |
FORWARD | 1 |
FORWARD |
BACKWARD | 2 |
BACKWARD |
NAS DUAS | 3 |
NAS DUAS |