Ordem de linha e de coluna

Na computação, ordem de linha e ordem de coluna são métodos para armazenar matrizes multidimensionais [en] em armazenamento linear, como a memória de acesso aleatório (RAM).
A diferença entre as ordens reside em quais elementos de um arranjo (array) são contíguos na memória. Na ordem de linha, os elementos consecutivos de uma linha residem próximos uns dos outros, enquanto o mesmo vale para elementos consecutivos de uma coluna na ordem de coluna. Embora os termos aludam às linhas e colunas de um arranjo bidimensional, ou seja, uma matriz, as ordens podem ser generalizadas para arranjos de qualquer dimensão observando que os termos ordem de linha e ordem de coluna são equivalentes às ordens lexicográfica e colexicográfica, respectivamente. As matrizes, sendo comumente representadas como coleções de vetores de linha ou coluna, usando essa abordagem, são efetivamente armazenadas como vetores consecutivos ou componentes vetoriais consecutivos. Tais formas de armazenar dados são referidas como AoS e SoA [en], respectivamente.
O layout de dados é crítico para passar arranjos corretamente entre programas escritos em diferentes linguagens de programação. Também é importante para o desempenho ao percorrer um arranjo porque as CPUs modernas processam dados sequenciais de forma mais eficiente do que dados não sequenciais. Isso se deve principalmente ao cache da CPU, que explora a localidade de referência espacial.[1] Além disso, o acesso contíguo torna possível usar instruções SIMD (instrução única, dados múltiplos) que operam em vetores de dados. Em algumas mídias, como armazenamento de dados em fita magnética [en], o acesso sequencial é ordens de magnitude mais rápido do que o acesso não sequencial.[carece de fontes]
Explicação e exemplo
Os termos ordem de linha (row-major) e ordem de coluna (column-major) derivam da terminologia relacionada à ordenação de objetos. Uma maneira geral de ordenar objetos com muitos atributos é primeiro agrupá-los e ordená-los por um atributo e, em seguida, dentro de cada grupo, agrupá-los e ordená-los por outro atributo, etc. Se mais de um atributo participar da ordenação, o primeiro seria chamado de maior (major) e o último de menor (minor). Se dois atributos participarem da ordenação, é suficiente nomear apenas o atributo maior.
No caso de arranjos, os atributos são os índices ao longo de cada dimensão. Para matrizes em notação matemática, o primeiro índice indica a linha e o segundo indica a coluna, por exemplo, dada uma matriz , a entrada está em sua primeira linha e segunda coluna. Essa convenção é transportada para a sintaxe em linguagens de programação,[2] embora frequentemente com índices começando em 0 [en] em vez de 1.[3]
Embora a linha seja indicada pelo primeiro índice e a coluna pelo segundo índice, nenhuma ordem de agrupamento entre as dimensões é implícita por isso. A escolha de como agrupar e ordenar os índices, seja pelos métodos de ordem de linha ou ordem de coluna, é, portanto, uma questão de convenção. A mesma terminologia pode ser aplicada a arranjos de dimensões ainda maiores. O agrupamento por ordem de linha começa no índice mais à esquerda e o por ordem de coluna no índice mais à direita, levando a ordens lexicográfica e colexicográfica (ou colex), respectivamente.
Por exemplo, a matriz
poderia ser armazenada de duas maneiras possíveis:
| Endereço | Ordem de linha | Ordem de coluna |
|---|---|---|
| 0 | ||
| 1 | ||
| 2 | ||
| 3 | ||
| 4 | ||
| 5 |
As linguagens de programação lidam com isso de maneiras diferentes. Em C, arranjos multidimensionais são armazenados em ordem de linha, e os índices do arranjo são escritos com a linha primeiro (ordem de acesso lexicográfica):
Endereçox + N_x*y
|
AcessoA[y][x]
|
Valor |
|---|---|---|
| 0 | A[0][0]
|
|
| 1 | A[0][1]
|
|
| 2 | A[0][2]
|
|
| 3 | A[1][0]
|
|
| 4 | A[1][1]
|
|
| 5 | A[1][2]
|
Por outro lado, em Fortran, os arranjos são armazenados em ordem de coluna, enquanto os índices do arranjo ainda são escritos com a linha primeiro (ordem de acesso colexicográfica):
Endereçoy + N_y*(x-1)
|
AcessoA(y,x)
|
Valor |
|---|---|---|
| 1 | A(1,1)
|
|
| 2 | A(2,1)
|
|
| 3 | A(1,2)
|
|
| 4 | A(2,2)
|
|
| 5 | A(1,3)
|
|
| 6 | A(2,3)
|
Observe como o uso de A[i][j] com indexação em várias etapas como em C, ao contrário de uma notação neutra como A(i,j) como em Fortran, quase inevitavelmente implica ordem de linha por razões sintáticas, por assim dizer, porque pode ser reescrito como (A[i])[j], e a parte da linha A[i] pode até ser atribuída a uma variável intermediária que é então indexada em uma expressão separada. (Nenhuma outra implicação deve ser assumida, por exemplo, Fortran não é ordem de coluna simplesmente por causa de sua notação, e mesmo a implicação acima poderia ser intencionalmente contornada em uma nova linguagem.)
Para usar a ordem de coluna em um ambiente de ordem de linha, ou vice-versa, por qualquer motivo, uma solução alternativa é atribuir papéis não convencionais aos índices (usando o primeiro índice para a coluna e o segundo índice para a linha), e outra é ignorar a sintaxe da linguagem calculando explicitamente as posições em um arranjo unidimensional. Obviamente, desviar da convenção provavelmente incorre em um custo que aumenta com o grau de interação necessária com os recursos da linguagem convencional e outros códigos, não apenas na forma de maior vulnerabilidade a erros (esquecer de também inverter a ordem de multiplicação da matriz, reverter para a convenção durante a manutenção do código, etc.), mas também na forma de ter que reorganizar ativamente os elementos, o que deve ser pesado contra qualquer propósito original, como aumentar o desempenho. Executar o loop no sentido da linha é preferível em linguagens de ordem de linha como C e vice-versa para linguagens de ordem de coluna.
Linguagens de programação e bibliotecas
Linguagens de programação ou suas bibliotecas padrão que suportam arranjos multidimensionais normalmente têm uma ordem de armazenamento nativa de linha ou coluna para esses arranjos.
A ordem de linha é usada em C/C++/Objective-C (para arranjos estilo C), PL/I,[4] Pascal,[5] Speakeasy,[carece de fontes] e SAS [en].[6]
A ordem de coluna é usada em Fortran,[7][8] IDL [en],[7] MATLAB,[8] GNU Octave, Julia,[9] S, S-PLUS,[10] R,[11] Scilab,[12] Yorick e rasdaman [en].[13]
Nem ordem de linha nem ordem de coluna
Uma alternativa típica para armazenamento de arranjos densos é usar vetores de Iliffe [en], que normalmente armazenam ponteiros para elementos na mesma linha de forma contígua (como ordem de linha), mas não as linhas em si. Eles são usados em (ordenados por idade): Java,[14] C#/CLI/.Net, Scala,[15] e Swift.
Ainda menos denso é usar listas de listas, por exemplo, em Python,[16] e na Wolfram do Wolfram Mathematica.[17]
Uma abordagem alternativa usa tabelas de tabelas, por exemplo, em Lua.[18]
Bibliotecas externas
O suporte para arranjos multidimensionais também pode ser fornecido por bibliotecas externas, que podem até suportar ordenações arbitrárias, onde cada dimensão tem um valor de passo (stride), e a ordem de linha ou ordem de coluna são apenas duas interpretações resultantes possíveis.
A ordem de linha é o padrão no NumPy[19] (para Python).
A ordem de coluna é o padrão no Eigen [en][20] e Armadillo (ambos para C++).
Um caso especial seria o OpenGL (e OpenGL ES) para processamento gráfico. Como "tratamentos matemáticos recentes de álgebra linear e campos relacionados invariavelmente tratam vetores como colunas", o designer Mark Segal decidiu substituir isso pela convenção no predecessor IRIS GL [en], que era escrever vetores como linhas; para compatibilidade, as matrizes de transformação ainda seriam armazenadas em ordem de vetor-maior (=ordem de linha) em vez de coordenada-maior (=ordem de coluna), e ele então usou o truque "[de] dizer que as matrizes no OpenGL são armazenadas em ordem de coluna".[21] Isso era realmente relevante apenas para apresentação, porque a multiplicação de matrizes era baseada em pilha e ainda podia ser interpretada como pós-multiplicação, mas, pior, a realidade vazou através da API baseada em C porque elementos individuais seriam acessados como M[vetor][coordenada] ou, efetivamente, M[coluna][linha], o que infelizmente confundiu a convenção que o designer procurava adotar, e isso foi até preservado na OpenGL Shading Language que foi adicionada posteriormente (embora isso também torne possível acessar coordenadas por nome, por exemplo, M[vetor].y). Como resultado, muitos desenvolvedores agora simplesmente declararão que ter a coluna como o primeiro índice é a definição de ordem de coluna, embora este claramente não seja o caso com uma linguagem de ordem de coluna real como Fortran.
Torch [en] (para Lua) mudou da ordem de coluna[22] para a ordem de linha[23] padrão.
Transposição
Como trocar os índices de um arranjo é a essência da transposição de arranjo, um arranjo armazenado como ordem de linha, mas lido como ordem de coluna (ou vice-versa), parecerá transposto. Como a realização real desse rearranjo na memória [en] é tipicamente uma operação cara, alguns sistemas fornecem opções para especificar matrizes individuais como sendo armazenadas transpostas. O programador deve então decidir se deve ou não reorganizar os elementos na memória, com base no uso real (incluindo o número de vezes que o arranjo é reutilizado em um cálculo).
Por exemplo, as funções BLAS [en] recebem sinalizadores indicando quais arranjos estão transpostos.[24]
Cálculo de endereço em geral
O conceito generaliza para arranjos com mais de duas dimensões.
Para um arranjo de dimensão d com dimensões Nk (k=1...d), um dado elemento desse arranjo é especificado por uma tupla de d índices (baseados em zero) .
Na ordem de linha, a última dimensão é contígua, de modo que o deslocamento de memória (memory-offset) desse elemento é dado por:
Na ordem de coluna, a primeira dimensão é contígua, de modo que o deslocamento de memória desse elemento é dado por: onde o produto vazio é o elemento neutro multiplicativo, ou seja, .
Para uma dada ordem, o passo [en] (stride) na dimensão k é dado pelo valor de multiplicação entre parênteses antes do índice nk nas somas do lado direito acima.
Mais geralmente, existem d! ordens possíveis para um determinado arranjo, uma para cada permutação de dimensões (com ordem de linha e ordem de coluna sendo apenas 2 casos especiais), embora as listas de valores de passo não sejam necessariamente permutações uma da outra, por exemplo, no exemplo 2 por 3 acima, os passos são (3,1) para ordem de linha e (1,2) para ordem de coluna.
Ver também
- Arranjo (estrutura de dados)
- Comparação de linguagens de programação (arranjo) [en]
- Matriz esparsa
- Ordem de Morton [en], outra maneira de mapear dados multidimensionais para um índice unidimensional
- Origem do índice [en], outra diferença entre os tipos de arranjo em diferentes linguagens de programação
- Representação de matriz [en]
- Vetorização (matemática) [en], o equivalente a transformar uma matriz no vetor de ordem de coluna correspondente
Referências
- ↑ «Cache Memory». Peter Lars Dordal. Consultado em 10 de abril de 2021
- ↑ «Arrays and Formatted I/O». FORTRAN Tutorial. Consultado em 19 de novembro de 2016
- ↑ «Why numbering should start at zero». E. W. Dijkstra Archive. Consultado em 2 de fevereiro de 2017
- ↑ «Language Reference Version 4 Release 3» (PDF). IBM. Consultado em 13 de novembro de 2017.
Initial values specified for an array are assigned to successive elements of the array in row-major order (final subscript varying most rapidly).
- ↑ «ISO/IEC 7185:1990(E)» (PDF).
An array-type that specifies a sequence of two or more index-types shall be an abbreviated notation for an array-type specified to have as its index-type the first index-type in the sequence and to have a component-type that is an array-type specifying the sequence of index-types without the first index-type in the sequence and specifying the same component-type as the original specification.
- ↑ «SAS® 9.4 Language Reference: Concepts, Sixth Edition» (PDF). SAS Institute Inc. 6 de setembro de 2017. p. 573. Consultado em 18 de novembro de 2017.
From right to left, the rightmost dimension represents columns; the next dimension represents rows. [...] SAS places variables into a multidimensional array by filling all rows in order, beginning at the upper left corner of the array (known as row-major order).
- ↑ a b «Columns, Rows, and Array Majority». www.nv5geospatialsoftware.com. Consultado em 31 de julho de 2024
- ↑ a b Documentação MATLAB, MATLAB Data Storage (acessado de Mathworks.co.uk, janeiro de 2014).
- ↑ «Multi-dimensional Arrays». Julia. Consultado em 9 de novembro de 2020
- ↑ Spiegelhalter et al. (2003, p. 17): Spiegelhalter, David; Thomas, Andrew; Best, Nicky; Lunn, Dave (Janeiro de 2003), «Formatting of data: S-Plus format», WinBUGS User Manual (PDF) Version 1.4 ed. , Cambridge, UK: MRC Biostatistics Unit, Institute of Public Health, arquivado do original (PDF) em 18 de maio de 2003
- ↑ An Introduction to R, Seção 5.1: Arrays (acessado em março de 2010).
- ↑ «FFTs with multidimensional data». Scilab Wiki. Consultado em 25 de novembro de 2017.
Because Scilab stores arrays in column major format, the elements of a column are adjacent (i.e. a separation of 1) in linear format.
- ↑ «Internal array representation in rasdaman». rasdaman.org. Consultado em 30 de março de 2025
- ↑ «Java Language Specification». Oracle. Consultado em 13 de fevereiro de 2016
- ↑ «object Array». Scala Standard Library. Consultado em 1 de maio de 2016
- ↑ «The Python Standard Library: 8. Data Types». Consultado em 18 de novembro de 2017
- ↑ «Vectors and Matrices». Wolfram. Consultado em 12 de novembro de 2017
- ↑ «11.2 – Matrices and Multi-Dimensional Arrays». Consultado em 6 de fevereiro de 2016
- ↑ «The N-dimensional array (ndarray)». SciPy.org. Consultado em 3 de abril de 2016
- ↑ «Eigen: Storage orders». eigen.tuxfamily.org. Consultado em 23 de novembro de 2017.
If the storage order is not specified, then Eigen defaults to storing the entry in column-major.
- ↑ «Column Vectors Vs. Row Vectors». Consultado em 12 de novembro de 2017
- ↑ «Tensor». Consultado em 6 de fevereiro de 2016
- ↑ «Tensor». Torch Package Reference Manual. Consultado em 8 de maio de 2016
- ↑ «BLAS (Basic Linear Algebra Subprograms)». Consultado em 16 de maio de 2015
Fontes
- Donald E. Knuth, The Art of Computer Programming Volume 1: Fundamental Algorithms, third edition, section 2.2.6 (Addison-Wesley: New York, 1997).