-
-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Completing the StridedArray interface #186
Comments
It's a tough call; on one hand it seems like "internal implementation detail" but on the other hand we export |
Indeed, I could also see the other proposal work. Stop exporting |
@Jutho The value of strided array is not limited to BLAS. There are many C libraries out there that require strided or contiguous memory layout. Unexporting such types would discourage new packages that work with such C libraries. I think we should clearly define the semantics of a strided array, and standardize the interface. From a syntax standpoint, all strided array types should provide these functions (in addition to those that provided by all arrays): |
I agree that the concept of strided arrays is very useful. Both for C libraries, but maybe also for pure julia methods. The main point about this issue is that the interface for accessing a strided array is not complete. Either strided array is only for C libraries, then |
This added documentation for a strided array interface consisting of overriding I may put together another pull request to deprecate abstract type ArrayMemoryLayout{T} end
struct UnknownLayout{T} <: ArrayMemoryLayout{T} end
struct StridedLayout{T,trans} <: ArrayMemoryLayout{T} end
struct SymmetricStridedLayout{T} <: ArrayMemoryLayout{T} end
struct UpperTriangularLayout{T} <: ArrayMemoryLayout{T} end
memorylayout(::Type{A}) where A <: AbstractArray{T} where T = UnknownLayout{T}()
memorylayout(::Type{A}) where A <: Array{T} where T = StridedLayout{T,:N}()
memorylayout(A::AbstractArray) = memorylayout(typeof(A))
... Then linear algebra routines will dispatch to BLAS and LAPACK when Note that this construction is not quite the same as a trait/interface as memory layout is always unique, whereas a single array can have multiple traits/interfaces. I still need to think how to handle adjoints/transposes, but maybe something like this will work: adjoint(::ArrayMemoryLayout{T}) where T = UnknownLayout{T}()
adjoint(::StridedLayout{T,:N}) where T = StridedLayout{T,:C}()
memorylayout(::Type{A}) where A <: Adjoint{T, AA} where {T, AA<:AbstractArray} = adjoint(memorylayout(AA)) |
Yes, the big question is what the kinds of properties are important enough to make it into the trait hierarchy and how that gets put together. In general we'd been thinking that this trait can layer onto 1.x as a new feature, but I'd most value a second set of eyes in making sure that things are indeed structured in such a way to support that. My current thought is that this will require multiple traits that work together for different orthogonal dimensions of variance in how arrays get stored. Here's a start of a document on what I've been thinking: https://gist.github.com/mbauman/3fa8a1ac00f574480968a4f47be2c361 |
Let me reiterate my main point: "trait" isn't the right word here since memory storage is unique, hence we can ask a type what it is. So instead of querying I'm imagining that instead of the current code mul!(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}) where {T<:BlasFloat} = gemv!(y, 'N', A, x)
mul!(y::StridedVector{T}, transA::Transpose{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}) where {T<:BlasFloat} = (A = transA.parent; gemv!(y, 'T', A, x)) we would have mul!(y, A, x) = _mul!(y, A, x, memoryformat(y), memoryformat(A), memoryformat(x))
_mul!(y::AbstractVector{T}, A::AbstractMatrix{T}, x::AbstractVector{T}, ::StridedFormat{T,:N}, ::StridedFormat{T,:N}, ::StridedFormat{T,:N}) where T<:BlasFloat = gemv!(y, 'N', A, x)
_mul!(y::AbstractVector{T}, A::AbstractMatrix{T}, x::AbstractVector{T}, ::StridedFormat{T,:N}, ::StridedFormat{T,:T}, ::StridedFormat{T,:N}) where T<:BlasFloat = gemv!(y, 'T', A, x)
_mul!(y::AbstractVector, A::AbstractMatrix, x::AbstractVector, ::UnknownFormat, ::StridedFormat, ::StridedFormat)= # native julia fallback (We don't need the |
Yup, you have roughly the same vision I do. I would just call |
It’s only a point about semantics. “Traits” implies possibly multiple traits at the same time (a matrix can be banded and upper triangular at the same time) whereas the actual storage in memory is unique (the entries of a banded and upper triangular matrix could be stored either in either |
Also mentioned in JuliaLang/julia#10889: There might be some advantage to making it really a hierarchy instead of just a bunch of mutually exclusive types, e.g. abstract type ArrayMemoryLayout{T} end
abstract type UnknownMemoryLayout{T} <: AbstractMemoryLayout{T} end
abstract type StridedFormat{T,...} <: AbstractMemoryLayout{T} end
abstract type UnitStridedFormat{T,...} <: StridedFormat{T} end
struct Contiguous{T,...} <: UnitStridedFormat{T} end since some routines just require strided, other require strided with unit first stride, and e.g. linear indexing requires contiguous layout. The downside is that you need to use the type itself instead of the singleton instance as trait (unless you also make a concrete Note added: I think that's also the contents of gist of @mbaumann, and so there should be an additional level between |
@dlfivefifty , thanks for all your nice work for completing the strided interface. I do have a question/request, after reading It would be good if information about the strided memory layout is not only something to link to external C or Fortran libraries, but can also be used in Julia algorithms, i.e. to write native Julia algorithms that access memory in an optimized way. For plain Maybe |
Is your suggestion that |
That is already the case in Base. But |
It is in PR JuliaLang/julia#25558: the order memory is accessed by |
I'm not sure
@Jutho How do you use |
I've always been a bit suspicious of the array |
Here’s an example: both |
Amusingly, I was looking for a solution to a broken test for one of my PRs and found an attempt to use JuliaLang/julia#15209 (comment) Even then, it's not sufficient for that task as some arrays have multiple "parents" — like Bidiagonal's two vectors or (in the context of aliasing detection) SubArray's indices. |
My main use case is the algorithms The easiest way to do this is to work with strides directly and compute some kind of linear index (memory offset) for each of the arrays involved. But how should you then use this index, in a Julia algorithm. Doing
The fact that |
C.f. ArrayInterface.jl (and things that build on it like StrideArrays.jl) |
Many methods in Julia base, especially those that interface with external C libraries like BLAS, LAPACK and FFTW are only defined for
StridedArray
, which currently is aUnion
type. It has been argued before by @timholy that properties likestrided
andcontiguous
should actually be a trait (a THT).However, my main question is about the interface for a
StridedArray
, which is currently incomplete and somewhat inconsistent. The manual describesStridedArray
as being an arrayA
such that, increasing indexk
by 1 is equivalent to linear indexing where the linear index is increased bystride(A,k)
. But that is of course not true, since linear indexing is expected to be such that the linear index increases byprod(size(A)[1:k-1])
. A simple example isA=rand(5,5);B=sub(A,1:3,1:3)
. Clearly,stride(B,2)=5
, but linear indexing withi+5*(j-1)
would not give me element(i,j)
, you need to index withi+3*(j-1)
for that. In that respect,stride(A,k)
is also defined for a generalAbstractArray
and just returnsprod(size(A)[1:k-1])
, which does not correspond to its description in the manual as "distance in memory".So what is the role of
StridedArray
in Julia. Is it only to interface with C libraries and to be used in combination withpointer
? It is certainly useful to also exploit the stridedness in pure Julia methods, see e.g.permutedims!
. Currently, this is dealt with by checking explicitly whether theStridedArray
is aSubArray
, and in that case, to extract the underlying parent array and the offset, and then apply linear indexing to that. I noticed thatparent
is now part of the exported method list, but there is no corresponding method to get theoffset
orfirst_index
. Maybe it would be good to have this method as well and then define these methods (stride
andstrides
,parent
,offset
?) as the interface for a strided array (as type or as trait).The text was updated successfully, but these errors were encountered: