Les tableaux de taille fixe sont déjà spécialisés en fonction de la taille. Ces tableaux ne sont pas appropriés lorsque le nombre de lignes est variable, N
dans votre cas, peut varier. Les problèmes de performance que vous constatez sont probablement dus à surspécialisation .
Permettez-moi d'être un peu plus précis.
Le compilateur Julia est capable de réaliser une abstraction à coût nul grâce à une spécialisation agressive sur les types d'arguments. Ainsi, en général (c'est-à-dire dans tous les cas à l'exception de quelques-uns où la spécialisation serait trop coûteuse ou est explicitement désactivée), si une fonction est appelée avec deux signatures de type différentes, deux versions de cette fonction seront compilées.
Étant donné que la taille d'un Mat
fait partie de son type, ce qui signifie qu'une version sera compilée pour chaque taille possible de l'élément Mat
. La spécialisation que vous recherchez est donc déjà réalisée.
La spécialisation n'est toutefois pas gratuite. Elle entraîne deux coûts :
- La première fois qu'une fonction est appelée sur une signature particulière, de la mémoire est allouée et le compilateur doit s'exécuter.
- Lorsqu'un paramètre dont le type ne peut être déduit est transmis à une fonction, il y a "instabilité de type" et une répartition dynamique est nécessaire. La répartition dynamique implique des recherches au moment de l'exécution.
Ainsi, si vos matrices sont de la taille (2, N)
donde N
varie et n'est pas connu au moment de la compilation, le coût de la performance de la répartition dynamique seront encourus. Ce coût de performance peut être limité en utilisant la technique de la barrière de fonction : nous n'encourons ce coût qu'une seule fois pour chaque appel de type instable, de sorte que la limitation du nombre de ces appels améliore la performance.
Mais ce qui augmenterait encore plus les performances serait d'éviter complètement cette répartition dynamique. Il est possible de construire un type de tableau qui n'encode que le nombre de colonnes dans le type, et qui a le nombre de lignes comme champ au moment de l'exécution. En d'autres termes, il est possible que votre problème de performance soit dû à une surspécialisation et que vous deviez créer vos types de manière à réduire le degré de spécialisation.
Trouver le bon équilibre est essentiel pour tirer le maximum de performances d'une application. La spécialisation sur la taille d'un tableau est en fait rarement utile - même les codes C et C++, par exemple, ont tendance à passer les tailles de tableau en tant que paramètres d'exécution, au lieu de se spécialiser sur une taille de tableau particulière. Cela n'est pas très coûteux. Dans la plupart des cas, FixedSizeArrays.jl
n'améliorera pas les performances, mais les nuira au contraire. Il y a certainement des situations où cela peut aider, mais la vôtre n'est peut-être pas l'une d'entre elles.
Dans votre cas, pour une performance maximale, je pense qu'un type comme celui-ci serait le plus rapide :
immutable TwoColumnMatrix{T, BaseType} <: AbstractArray{T, 2}
height::Int
base::BaseType
end
function TwoColumnMatrix(A::Matrix)
size(A, 2) == 2 || throw(ArgumentError("must be two columns"))
TwoColumnMatrix{eltype(A), typeof(A)}(size(A, 1), A)
end
Base.@propagate_inbounds function getindex(M::TwoColumnMatrix, n::Int)
M.base[n]
end
size(M::TwoColumnMatrix) = (M.height, 2)
Il se peut que vous deviez définir des méthodes supplémentaires pour obtenir des performances maximales et, comme toujours, procédez à des analyses comparatives. Il est possible que le surcoût du wrapper ne vaille pas la peine que le compilateur connaisse les dimensions.