comparison Notes.md @ 1360:f59228534d3a tooling/benchmarks

Merge default
author Jonatan Werpers <jonatan@werpers.com>
date Sat, 20 May 2023 15:15:22 +0200
parents 08f06bfacd5c
children 4684c7f1c4cb
comparison
equal deleted inserted replaced
1321:42738616422e 1360:f59228534d3a
139 - [ ] Check how the native julia doc generator works 139 - [ ] Check how the native julia doc generator works
140 - [ ] Check if Vidars design docs fit in there 140 - [ ] Check if Vidars design docs fit in there
141 - [ ] Create a macro @lazy which replaces a binary op (+,-) by its lazy equivalent? Would be a neat way to indicate which evaluations are lazy without cluttering/confusing with special characters. 141 - [ ] Create a macro @lazy which replaces a binary op (+,-) by its lazy equivalent? Would be a neat way to indicate which evaluations are lazy without cluttering/confusing with special characters.
142 - [ ] Dispatch on Lower() instead of the type Lower so `::Lower` instead of `::Type{Lower}` ??? 142 - [ ] Dispatch on Lower() instead of the type Lower so `::Lower` instead of `::Type{Lower}` ???
143 Seems better unless there is some specific reason to use the type instead of the value. 143 Seems better unless there is some specific reason to use the type instead of the value.
144 - [ ] How do we handle mixes of periodic and non-periodic grids? Seems it should be supported on the grid level and on the 1d operator level. Between there it should be transparent.
145 - [ ] Can we have a trait to tell if a LazyTensor is transposable? 144 - [ ] Can we have a trait to tell if a LazyTensor is transposable?
146 - [ ] Is it ok to have "Constructors" for abstract types which create subtypes? For example a Grids() functions that gives different kind of grids based on input? 145 - [ ] Is it ok to have "Constructors" for abstract types which create subtypes? For example a Grids() functions that gives different kind of grids based on input?
147 - [ ] Figure out how to treat the borrowing parameters of operators. Include in into the struct? Expose via function dispatched on the operator type and grid? 146 - [ ] Figure out how to treat the borrowing parameters of operators. Include in into the struct? Expose via function dispatched on the operator type and grid?
148 147
149 ## Identifiers for regions 148 ## Identifiers for regions
150 The identifiers (`Upper`, `Lower`, `Interior`) used for region indecies should probabily be included in the grid module. This allows new grid types to come with their own regions. 149 The identifiers (`Upper`, `Lower`, `Interior`) used for region indecies should probably be included in the grid module. This allows new grid types to come with their own regions.
150 We implement this by refactoring RegionIndices to be agnostic to the region types and then moving the actual types to Grids.
151 151
152 ## Regions and tensormappings 152 ## Regions and tensormappings
153 - [ ] Use a trait to indicate if a LazyTensor uses indices with regions. 153 - [ ] Use a trait to indicate if a LazyTensor uses indices with regions.
154 The default should be that they do NOT. 154 The default should be that they do NOT.
155 - [ ] What to name this trait? Can we call it IndexStyle but not export it to avoid conflicts with Base.IndexStyle? 155 - [ ] What to name this trait? Can we call it IndexStyle but not export it to avoid conflicts with Base.IndexStyle?
196 Does it make sense to have boundschecking only in getindex methods? 196 Does it make sense to have boundschecking only in getindex methods?
197 This would mean no bounds checking in applys, however any indexing that they do would be boundschecked. The only loss would be readability of errors. But users aren't really supposed to call apply directly anyway. 197 This would mean no bounds checking in applys, however any indexing that they do would be boundschecked. The only loss would be readability of errors. But users aren't really supposed to call apply directly anyway.
198 198
199 Preferably dimensions and sizes should be checked when lazy objects are created, for example TensorApplication, TensorComposition and so on. If dimension checks decreases performance we can make them skippable later. 199 Preferably dimensions and sizes should be checked when lazy objects are created, for example TensorApplication, TensorComposition and so on. If dimension checks decreases performance we can make them skippable later.
200 200
201 ## Changes to `eval_on`
202 There are reasons to replace `eval_on` with regular `map` from Base, and implement a kind of lazy map perhaps `lmap` that work on indexable collections.
203
204 The benefit of doing this is that we can treat grids as gridfunctions for the coordinate function, and get a more flexible tool. For example `map`/`lmap` can then be used both to evaluate a function on the grid but also get a component of a vector valued grid function or similar.
205
206 A question is how and if we should implement `map`/`lmap` for functions like `(x,y)->x*y` or stick to just using vector inputs. There are a few options.
207
208 * use `Base.splat((x,y)->x*y)` with the single argument `map`/`lmap`.
209 * implement a kind of `unzip` function to get iterators for each component, which can then be used with the multiple-iterators-version of `map`/`lmap`.
210 * Inspect the function in the `map`/`lmap` function to determine which matches.
211
212 Below is a partial implementation of `lmap` with some ideas
213 ```julia
214 struct LazyMapping{T,IT,F}
215 f::F
216 indexable_iterator::IT # ___
217 end
218
219 function LazyMapping(f,I)
220 IT = eltype(I)
221 T = f(zero(T))
222 F = typeof(f)
223
224 return LazyMapping{T,IT,F}(f,I)
225 end
226
227 getindex(lm::LazyMapping, I...) = lm.f(lm.I[I...])
228 # indexabl interface
229 # iterable has shape
230
231 iterate(lm::LazyMapping) = _lazy_mapping_iterate(lm, iterate(lm.I))
232 iterate(lm::LazyMapping, state) = _lazy_mapping_iterate(lm, iterate(lm.I, state))
233
234 _lazy_mapping_iterate(lm, ::Nothing) = nothing
235 _lazy_mapping_iterate(lm, (next, state)) = lm.f(next), state
236
237 lmap(f, I) = LazyIndexableMap(f,I)
238 ```
239
240 The interaction of the map methods with the probable design of multiblock functions involving nested indecies complicate the picture slightly. It's clear at the time of writing how this would work with `Base.map`. Perhaps we want to implement our own versions of both eager and lazy map.
241
242 ## Multiblock implementation
243 We want multiblock things to work very similarly to regular one block things.
244
245 ### Grid functions
246 Should probably support a nested indexing so that we first have an index for subgrid and then an index for nodes on that grid. E.g `g[1,2][2,3]` or `g[3][43,21]`.
247
248 We could also possibly provide a combined indexing style `g[1,2,3,4]` where the first group of indices are for the subgrid and the remaining are for the nodes.
249
250 We should make sure the underlying buffer for gridfunctions are continuously stored and are easy to convert to, so that interaction with for example DifferentialEquations is simple and without much boilerplate.
251
252 #### `map` and `collect` and nested indexing
253 We need to make sure `collect`, `map` and a potential lazy map work correctly through the nested indexing.
254
255 ### Tensor applications
256 Should behave as grid functions
257
258 ### LazyTensors
259 Could be built as a tuple or array of LazyTensors for each grid with a simple apply function.
260
261 Nested indexing for these is problably not needed unless it simplifies their own implementation.
262
263 Possibly useful to provide a simple type that doesn't know about connections between the grids. Antother type can include knowledge of the.
264
265 We have at least two option for how to implement them:
266 * Matrix of LazyTensors
267 * Looking at the grid and determining what the apply should do.
268
269 ### Overall design implications of nested indices
270 If some grids accept nested indexing there might be a clash with how LazyArrays work. It would be nice if the grid functions and lazy arrays that actually are arrays can be AbstractArray and things can be relaxed for nested index types.
271
201 ## Vector valued grid functions 272 ## Vector valued grid functions
202 Från slack konversation:
203
204 Jonatan Werpers:
205 Med vektorvärda gridfunktioner vill vi ju fortfarande att grid funktionen ska vara till exempel AbstractArray{LitenVektor,2}
206 Och att man ska kunna göra allt man vill med LitenVektor
207 typ addera, jämföra osv
208 Och då borde points returnera AbstractArray{LitenVektor{Float,2},2} för ett 2d nät
209 Men det kanske bara ska vara Static arrays?
210
211 Vidar Stiernström:
212 Ja, jag vet inte riktigt vad som är en rimlig representation
213 Du menar en vektor av static arrays då?
214
215 Jonatan Werpers:
216 Ja, att LitenVektor är en StaticArray
217
218 Vidar Stiernström:
219 Tuplar känns typ rätt inuitivt för att representera värdet i en punkt
220 men
221 det suger att man inte har + och - för dem
222
223 Jonatan Werpers:
224 Ja precis
225
226 Vidar Stiernström:
227 så kanske är bra med static arrays i detta fall
228
229 Jonatan Werpers:
230 Man vill ju kunna köra en Operator rakt på och vara klar eller?
231
232 Vidar Stiernström:
233 Har inte alls tänkt på hur det vi gör funkar mot vektorvärda funktioner
234 men känns som staticarrays är hur man vill göra det
235 tuplar är ju immutable också
236 blir jobbigt om man bara agerar på en komponent då
237
238 Jonatan Werpers:
239 Hm…
240 Tål att tänkas på
241 Men det lär ju bli mer indirektion med mutables eller?
242 Hur fungerar det?
243 Det finns ju hur som helst både SVector och MVector i StaticArrays
244
245 Vidar Stiernström:
246 När vi jobbat i c/c++ och kollat runt lite hur man brukar göra så lagrar man i princip alla sina obekanta i en lång vektor och så får man specificera i funktioerna vilken komponent man agerar på och till vilken man skriver
247 så man lagrar grejer enl: w = [u1, v1, u2, v2, …] i 1D.
248 Men alltså har ingen aning hur julia hanterar detta
249
250 Jonatan Werpers:
251 Det vi är ute efter kanske är att en grid funcktion är en AbstractArray{T,2} where T<:NågotSomViKanRäknaMed
252 Och så får den typen var lite vad som helst.
253
254 Vidar Stiernström:
255 Tror det kan vara farligt att ha nåt som är AbstractArray{LitenArray{NDof},Dim}
256 Jag gissar att det kompilatorn vill ha är en stor array med doubles
257
258 Jonatan Werpers:
259 Och sen är det upp till den som använder grejerna att vara smart
260 Vill man vara trixig kan man väl då imlementera SuperHaxxorGridFunction <: AbstractArray{Array{…},2} som lagrar allt linjärt eller något sånt
261 Det kommer väl lösa sig när man börjar implementera vektorvärda saker
262 Euler nästa!
263 New
264 Vidar Stiernström:
265 Det vore skönt att inte behöva skriva såhär varje gång man testar mot en tupel :smile: @test [gp[i]...] ≈ [p[i]...] atol=5e-13
266
267 Jonatan Werpers:
268 https://github.com/JuliaArrays/ArraysOfArrays.jl
269 https://github.com/jw3126/Setfield.jl
270 273
271 ### Test-applikationer 274 ### Test-applikationer
272 div och grad operationer 275 div- och grad-operationer
273 276
274 Enligt Wikipedia verkar det som att `∇⋅` agerar på första dimensionen av ett tensor fält och `div()` på sista. 277 Enligt Wikipedia verkar det som att `∇⋅` agerar på första dimensionen av ett tensorfält och `div()` på sista.
275 Om man generaliserar kanske `∇` i så fall bara lägger till en dimension i början. 278 Om man generaliserar kanske `∇` i så fall bara lägger till en dimension i början.
276 279
277 Kan vi implementera `⋅`(\cdot) så att de fungerar som man vill för både tensor-fält och tensor-operatorer? 280 Kan vi implementera `⋅`(\cdot) så att de fungerar som man vill för både tensor-fält och tensor-operatorer?
278 281
279 Är `∇` ett tensor-fält av tensor-operatorer? Vad är ett tensor-fält i vår kod? Är det en special-fall av en tensor-mapping? 282 Är `∇` ett tensor-fält av tensor-operatorer? Vad är ett tensor-fält i vår kod? Är det en special-fall av en tensor-mapping?
280 283
281 ### Grid-funktionen 284 ### Grid-funktionen
282 Grid-funktionon har typen `AbstractArray{T,2} where T`. 285 Grid-funktioner har typen `AbstractArray{T,2} where T`.
283 `T` kan vara lite vad som helst, tillexemel en SVector eller Array, eller tuple. TensorOperatorerna bryr sig inte om exakt vad det är, mer än att typen måste stödja de operationer som operatorn använder. 286 `T` kan vara lite vad som helst, tillexemel en SVector eller Array, eller Tuple. Tensoroperatorerna bryr sig inte om exakt vad det är, mer än att typen måste stödja de operationer som operatorn använder.
284 287
285 En nackdel kan vara hur man ska få ut gridfunktionen för tex andra komponenten. 288 En nackdel kan vara hur man ska få ut gridfunktionen för tex andra komponenten.
286 289
287 Syntax: 290 Syntax:
288 ``` 291 ```
290 gf = evalOn(g, f) 293 gf = evalOn(g, f)
291 gf[2,3] # x̄ för en viss gridpunkt 294 gf[2,3] # x̄ för en viss gridpunkt
292 gf[2,3][2] # x̄[2] för en viss gridpunkt 295 gf[2,3][2] # x̄[2] för en viss gridpunkt
293 ``` 296 ```
294 297
295 Note: Behöver bestämma om eval on skickar in `x̄` eller `x̄...` till `f`. Eller om man kan stödja båda.
296
297 ### Tensor operatorer 298 ### Tensor operatorer
298 Vi kan ha tensor-operatorer som agerar på ett skalärt fält och ger ett vektorfält eller tensorfält. 299 Vi kan ha tensor-operatorer som agerar på ett skalärt fält och ger ett vektorfält eller tensorfält.
299 Vi kan också ha tensor-operatorer som agerar på ett vektorfält eller tensorfält och ger ett skalärt fält. 300 Vi kan också ha tensor-operatorer som agerar på ett vektorfält eller tensorfält och ger ett skalärt fält.
300 301
301 TBD: Just nu gör `apply_transpose` antagandet att domän-typen är samma som range-typen. Det behöver vi på något sätt bryta. Ett alternativ är låta en LazyTensor ha `T_domain` och `T_range` istället för bara `T`. Känns dock lite grötigt. Ett annat alternativ skulle vara någon typ av trait för transpose? Den skulle kunna innehålla typen som transponatet agerar på? Vet inte om det fungerar dock. 302 TBD: Just nu gör `apply_transpose` antagandet att domän-typen är samma som range-typen. Det behöver vi på något sätt bryta. Ett alternativ är låta en LazyTensor ha `T_domain` och `T_range` istället för bara `T`. Känns dock lite grötigt. Ett annat alternativ skulle vara någon typ av trait för transpose? Den skulle kunna innehålla typen som transponatet agerar på? Vet inte om det fungerar dock.
307 308
308 Skulle kunna ha en funktion `range_type(::LazyTensor, ::Type{domain_type})` 309 Skulle kunna ha en funktion `range_type(::LazyTensor, ::Type{domain_type})`
309 310
310 Kanske kan man implementera `⋅(tm::LazyTensor{R,D}, v::AbstractArray{T,D})` där T är en AbstractArray, tm på något sätt har komponenter, lika många som T har element. 311 Kanske kan man implementera `⋅(tm::LazyTensor{R,D}, v::AbstractArray{T,D})` där T är en AbstractArray, tm på något sätt har komponenter, lika många som T har element.
311 312
312 ### Ratade alternativ:
313
314
315 #### 2.AbstractArray{T,2+1} where T (NOPE!)
316 Blir inte den här. Bryter mot alla tankar om hur grid funktioner ska fungera. Om de tillåts ha en annan dimension än nätet blir allt hemskt.
317
318 Man låter helt enkelt arrayen ha en extra dimension. En fördel är att man har en väldigt "native" typ. En nackdel kan vara att det eventuellt blir rörigt vilken dimension olika operatorer ska agera på. I värsta fall behöver vi "kroneckra in" de tillagda dimensionerna. Vektorfältets index kommer också att bli det första eftersom vi vill att de ska lagras kontinuerligt i minnet pga chachen. (Går kanske att lösa med en custom typ men då krånglar man till det för sig). En fördel skulle vara att man enkelt får ut olika komponenter.
319
320 Syntax:
321 ```
322 gf = eval_on_grid(g,f)
323 gf[:,2,3] # Hela vektorn för en gridpunkt
324 gf[2,2,3] # Andra komponenten av vektor fältet i en punkt.
325 gf[2,:,:] #
326 ```
327
328 ### Evaluering av funktioner på nät
329 Hur ska man skriva funktioner som evalueras på nätet? `f(x,y) = ...` eller `f(x̄) = ...`? Eller båda? Kan eval_on_grid se skillnad eller får användaren specificera?
330
331 ```
332 f(x,y) = [x^2, y^2]
333 f(x̄) = [x̄[1]^2, x̄[2]^2]
334 ```
335
336 Påverkas detta av hur vi förväntar oss kunna skapa lata gridfunktioner?
337
338 ### Komponenter som gridfunktioner 313 ### Komponenter som gridfunktioner
339 En viktig operation för vektor fält är att kunna få ut komponenter som grid-funktioner. Detta behöver antagligen kunna ske lazy. 314 En viktig operation för vektorfält är att kunna få ut komponenter som grid-funktioner. Detta behöver antagligen kunna ske lazy.
340 Det finns ett par olika lösningar: 315 Det finns ett par olika lösningar:
316 * Använda map eller en lazy map (se diskussion om eval_on)
341 * Implementera en egen typ av view som tar hand om detta. Eller Accessors.jl? 317 * Implementera en egen typ av view som tar hand om detta. Eller Accessors.jl?
342 * Använda en LazyTensor 318 * Använda en LazyTensor
343 * Någon typ av lazy-broadcast 319 * Någon typ av lazy-broadcast
344 * En lazy array som applicerar en funktion för varje element. 320 * En lazy array som applicerar en funktion för varje element.
345 321
346 Skulle vara en fördel om det är hyffsat generiskt så att en eventuell användare kan utöka det enkelt om de har någon egen exotisk typ. Eller ska man vila helt på 322
347 323 ### Prestanda-aspekter
348 Syntax: 324 [Vidar, Discord, 2023-03-03]
349 ``` 325 Typiskt sett finns det två sätt att representera vektorvärda gridfunktioner AbstractArray{T,Dim} där T är en vektor över komponenterna. Man skulle alltså i 1D ha
350 gf = eval(...) 326 u = [ [u1[x1], u2[x1]] , [u1[x2], u2[x2]], ... [u1[xN], u2[xN]]]. Detta brukar kallas array of structs (AoS). Alternativet är struct of arrays (SoA), där man har alla gridpunkter för en given komponent u = [[u1[x1], u1[x2]],... u1[xN]], [u2[x1], u2[x2], ... u2[xN]]].
351 component(gf,2) # Andra komponenten av en vektor 327
352 component(gf,2,3) # (2,3) elementet av en matris 328 Personligen tycker jag att AoS känns som den mer naturliga representationen? Det skulle göra det enklarare att parallelisera en vektorvärd gridfunktion över gridpunkterna, och om man opererar på olika komponenter i samma funktion så är det också bra ur en minnesaccess-synpunkt då dessa kommer ligga nära vandra i minnet. Problemet är att AoS sabbar vektorisering på CPU då två gridpunkter i en komponent ligger långt bort från varandra. Efter lite eftersökningar (och efter att snackat lite med Ossian) så verkar det ändå som att AoS är dåligt på GPU, där man vill att trådar typiskt sett utföra samma operation på närliggande minne.
353 component(gf,:,2) # Andra kolumnen av en matris 329
354 @ourview gf[:,:][2] 330 Vad tänker du kring detta ur ett interface-perspektiv? Jag hittade paketet https://github.com/JuliaArrays/StructArrays.jl som verkar erbjuda AoS-interface men SoA-minneslayout så det kanske kan vara något vi kan använda? Inte native-stödd på samma sätt som SVector, men verkar iaf utvecklas aktivt.
355 ``` 331
356 332 [Efter telefonsamtal] För optimal prestanda behöver vi antagligen se till att man kan räkna ut varje komponent i en punkt individuellt. Detta så att man har frihet att till exempel låta den innersta loopen hålla komponentindexet konstant för att underlätta intruktionsvektorisering.
357 ## Grids embedded in higher dimensions 333
358 334
359 For grids generated by asking for boundary grids for a regular grid, it would 335 [Vidare tankar]
360 make sense if these grids knew they were embedded in a higher dimension. They 336 * Det borde bara vara output-gridfunktionen som behöver special-indexeras? Det viktiga på inputsidan är att den är lagrad på rätt sätt i minnet.
361 would return coordinates in the full room. This would make sense when 337 * Det borde inte vara några problem att behålla det "optimala" interfacet (gf[1,1,1][2]) till gridfunktionerna. Om man verkligen behöver kan skapa parallella indexeringsmetoder som gör det man behöver, i.e, "deep indexing".
362 drawing points for example, or when evaluating functions on the boundary. 338 * Det är inte säkert att vi behöver göra något speciellt på outputsidan överhuvudtaget. Det känns inte orimligt att kompilatorn skulle kunna optimera bort den koden som räknar ut onödiga komponenter.
363 339 * Om vi behöver special-indexering kommer till exempel LazyTensorApplication att behöva implementera det.
364 Implementation of this is an issue that requires some thought. Adding an extra 340 * För att komma vidare med något mer avancerat behöver vi antagligen implementera några operatorer som ger och agerar på vektorvärda funktioner. Tex grad, elastiska operatorn, andra?
365 "Embedded" type for each grid would make it easy to understand each type but 341
366 contribute to "type bloat". On the other hand adapting existing types to
367 handle embeddedness would complicate the now very simple grid types. Are there
368 other ways of doing the implentation?
369 342
370 ## Performance measuring 343 ## Performance measuring
371 We should be measuring performance early. How does our effective cpu and memory bandwidth utilization compare to peak performance? 344 We should be measuring performance early. How does our effective cpu and memory bandwidth utilization compare to peak performance?
372 345
373 We should make these test simple to run for any solver. 346 We should make these test simple to run for any solver.
385 A different approach would be to include it as a trait for operators so that you can specify what the adjoint for that operator is. 358 A different approach would be to include it as a trait for operators so that you can specify what the adjoint for that operator is.
386 359
387 360
388 ## Name of the `VolumeOperator` type for constant stencils 361 ## Name of the `VolumeOperator` type for constant stencils
389 It seems that the name is too general. The name of the method `volume_operator` makes sense. It should return different types of `LazyTensor` specialized for the grid. A suggetion for a better name is `ConstantStencilVolumeOperator` 362 It seems that the name is too general. The name of the method `volume_operator` makes sense. It should return different types of `LazyTensor` specialized for the grid. A suggetion for a better name is `ConstantStencilVolumeOperator`
363
364
365 ## Implementation of LazyOuterProduct
366 Could the implementation of LazyOuterProduct be simplified by making it a
367 struct containing two or more LazyTensors? (using split_tuple in a similar way
368 as TensorGrid)