<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div></div><div>Just a question: how would/does allowing the reordering of fields affect the correctness and performance of the (de)serialization API added in Swift 4?</div><div><br>On Jul 9, 2017, at 6:21 PM, Jens Persson via swift-evolution <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br><br></div><blockquote type="cite"><div><div dir="ltr"><div>Thanks for that clarification John McCall.</div>My code is using a lot of generic structs (in which memory layout is important) though, an example would be:<div>struct Vector4<E> : Vector {</div><div> typealias Index = VectorIndex4</div><div> typealias Element = E</div><div> var e0, e1, e2, e3: Element</div><div> …</div><div>}</div><div>And I guess I'm out of luck trying to represent those as C structs?</div><div>So AFAICS it looks like it is currently impossible to write generic low level code in Swift, unless I just keep doing what I've been doing (It does currently work after all) knowing that it will probably break in some future versions of Swift. But in that possible future version of Swift, I could probably find a way to make it work again (using some possible explicit tools for layout control present in that version of Swift).</div><div>Correct?</div><div>/Jens</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Jul 9, 2017 at 11:41 PM, John McCall <span dir="ltr"><<a href="mailto:rjmccall@apple.com" target="_blank">rjmccall@apple.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><br><div><span class=""><blockquote type="cite"><div>On Jul 9, 2017, at 4:49 PM, Jens Persson via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>> wrote:</div><br class="m_-7384661522464151848Apple-interchange-newline"><div><div dir="ltr">Sorry for making so much off topic noise in this thread, but I made a mistake regarding the Metal tutorial:<div>Looking more carefully I see now that they rebuild a vertedData: [Float] from their vertices: [Vertex] using the floatBuffer() method of the Vertex struct, which returns an Array with the stored properties of Vertex in correct order.</div><div><br></div><div><div>While searching the internet about this I saw Joe Groff mentioning on Twitter that:</div><div>"We plan to sort fields in padding order to minimize size, and may also automatically pack bools and enums in bitfields."</div></div><div><br></div><div>So AFAICS my current image processing code is making the possibly invalid assumption that eg</div><div>struct S {</div><div> var a, b, c, d: Float</div><div>}</div><div>will have a memory layout of 4*4=16 bytes (stride and size == 16) and an alignment of 4, and most importantly that a, b, c, d will be in that order.</div></div></div></blockquote><div><br></div></span>This is currently true, but may not always be. We want to reserve the right to re-order the fields even if it doesn't improve packing — for example, if two fields are frequently accessed together, field reordering could yield substantial locality benefits. We've also discussed reordering fields to put them in a canonical order for resilience, since it's a little counter-intuitive that reordering the fields of a struct should be ABI-breaking. (There are arguments against doing that as well, of course — for example, the programmer may have chosen the current order for their own locality optimizations.)</div><div><br></div><div><span class=""><blockquote type="cite"><div><div dir="ltr"><div>It looks like I should be defining my structs (the ones for which memory layout is important) in C and import them.</div></div></div></blockquote><div><br></div></span>This should always work, yes.</div><div><br></div><div><span class=""><blockquote type="cite"><div><div dir="ltr"><div>Although I would be surprised if a Swift-struct containing only same-sized fields (all of the same primitive type) would be reordered, and such changes to the language would probably include some per-struct way to express some sort of layout control (since being able to define structs to be used for low level data manipulation is important in a systems language).</div></div></div></blockquote><div><br></div></span>Exactly. In the long term, Swift will have some explicit tools for layout control.</div><span class="HOEnZb"><font color="#888888"><div><br></div><div>John.</div></font></span><div><div class="h5"><div><br><blockquote type="cite"><div><div dir="ltr"><div><br></div><div>/Jens</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Jul 9, 2017 at 7:01 PM, Jens Persson via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><span>I should perhaps add that in my image processing code, I use code like this:</span><div><span><br><div><div>func withVImageBuffer<Data, R>(for table: Table<Data>, body: (vImage_Buffer) -> R) -> R</div><div> where</div><div> Data.Coordinate.Index == VectorIndex2</div><div>{</div><div> let vib = vImage_Buffer(</div><div> data: table.baseAddress,</div><div> height: vImagePixelCount(table.size.e1<wbr>),</div><div> width: vImagePixelCount(table.size.e0<wbr>),</div><div> rowBytes: table.stride.e1</div><div> )</div><div> return withExtendedLifetime(table) { body(vib) }</div><div>}</div></div><div><br></div><div>Here, Table<Data> is the raster image. Data.Coordinate == VectorIndex2 makes it a 2D table, and a Table's Data also has a type parameter Data.Value which can be eg one of the "pixel"-struct I showed before.</div><div>This works without any problems (I've tested and used the some variant of this type of code for years) but it would surely break if the memory layout of simple structs changed.</div><div><br></div><div>I can't see how this usage is much different from the one in the Metal tutorial. It too uses pointers to point into a data created using the (Swift) struct "Vertex", and the GPU hardware has its expectations on the memory layout of that data, so the code would break if the memory layout of the Vertex struct changed.</div><div><br></div></span><div>/Jens</div><div><br></div></div></div><div class="gmail_extra"><br><div class="gmail_quote"><span>On Sun, Jul 9, 2017 at 6:35 PM, Jens Persson <span dir="ltr"><<a href="mailto:jens@bitcycle.com" target="_blank">jens@bitcycle.com</a>></span> wrote:<br></span><div><div class="m_-7384661522464151848h5"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>I don't think I'm misunderstanding you, but I might be, so I'll add more detail:</div><div><br></div>If you look at the Metal article, you'll see that the (Swift) struct "Vertex" is used to specify the data that is sent to Metal for creating a buffer (using MTLDevice<span style="color:rgb(56,58,66);font-family:'Droid Sans Mono',sans-serif;font-size:12px;white-space:pre-wrap;background-color:rgb(250,250,250)">.makeBuffer</span>). The result that the GPU will produce surely depends on the fields of the Vertex struct (x, y, z, r, g, b, a) being in the specified order (ie swapping the red channel with the x coordinate would produce an unexpected result).<br><div><br></div><div>And regarding the second example, pixel structs used for manipulating raster image data. Manipulating raster image data presumably includes stuff like displaying to screen, loading and saving raster images.</div><div>I currently use this way of doing this right now without any problems, but if the order of the fields (eg a, r, g, b) should change in the future, then my code would break (the colors of the images would at least not come out as expected).<span class="m_-7384661522464151848m_-3742573560874670849HOEnZb"><font color="#888888"><br></font></span></div><span class="m_-7384661522464151848m_-3742573560874670849HOEnZb"><font color="#888888"><div><br></div><div>/Jens</div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div></font></span></div><div class="m_-7384661522464151848m_-3742573560874670849HOEnZb"><div class="m_-7384661522464151848m_-3742573560874670849h5"><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Jul 9, 2017 at 5:53 PM, Chris Lattner <span dir="ltr"><<a href="mailto:clattner@nondot.org" target="_blank">clattner@nondot.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><br><div><span><blockquote type="cite"><div>On Jul 9, 2017, at 12:23 AM, Jens Persson <<a href="mailto:jens@bitcycle.com" target="_blank">jens@bitcycle.com</a>> wrote:</div><br class="m_-7384661522464151848m_-3742573560874670849m_1299701481412508098m_939603476265006243Apple-interchange-newline"><div><div dir="ltr"><br><div class="gmail_extra"><div class="gmail_quote">On Sat, Jul 8, 2017 at 6:28 PM, Chris Lattner via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word">Hi Susan,<div><br></div><div>Swift does not currently specify a layout for Swift structs. You shouldn’t be using them for memory mapped i/o or writing to a file, because their layout can change. When ABI stability for fragile structs lands, you will be able to count on it, but until then something like this is probably a bad idea.</div><div><br></div><div>-Chris</div></div></blockquote></div><br></div><div class="gmail_extra">Does this imply that you should never use Swift structs to eg interact with Metal?</div></div></div></blockquote><div><br></div></span>No.</div><div><br></div><div><span><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra">This seems to be a very common practice. Here is a typical example (from a Metal tutorial at <a href="http://raywenderlich.com/" target="_blank">raywenderlich.com</a>):</div><div class="gmail_extra"><br></div><div class="gmail_extra"><div class="gmail_extra">struct Vertex {</div><div class="gmail_extra"> var x,y,z: Float // position data</div><div class="gmail_extra"> var r,g,b,a: Float // color data</div><div class="gmail_extra"><br></div><div class="gmail_extra"> func floatBuffer() -> [Float] {</div><div class="gmail_extra"> return [x,y,z,r,g,b,a]</div><div class="gmail_extra"> }</div><div class="gmail_extra">}</div></div></div></div></blockquote><div><br></div></span><div>This doesn’t appear to expose the layout of the struct.</div><span><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra">Also, does it imply that we cannot use structs (of only primitive types) like:</div><div class="gmail_extra"><br></div><div class="gmail_extra">struct RgbaFloatsLinearGamma {</div><div class="gmail_extra"> var r, g, b, a: Float</div><div class="gmail_extra"> …</div><div class="gmail_extra">}</div><div class="gmail_extra">struct BgraBytesSrgbGamma {</div><div class="gmail_extra"> var b, g, r, a: UInt8</div><div class="gmail_extra">}</div><div class="gmail_extra"><br></div><div class="gmail_extra">for manipulating raster image data?</div></div></div></blockquote><div><br></div></span><div>I don’t see why that would be a problem.</div><span><div><br></div><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra">I vaguely remember a swift evo discussion where it was concluded that such usage was considered OK provided the stored properties of the structs was only primitive types, but I can't find it now.</div><div class="gmail_extra"><br></div><div class="gmail_extra">Perhaps it could be considered OK at least when the intended platforms are known to be only iOS devices?</div></div></div></blockquote><br></span></div><div>I think you’re misunderstanding what I’m saying. It isn’t correct to take (e.g.) an unsafepointer to the beginning of a struct, and serialize that out to disk, and expect that the fields are emitted in some order with some specific padding between them. None of the uses above try to do this.</div><span class="m_-7384661522464151848m_-3742573560874670849m_1299701481412508098HOEnZb"><font color="#888888"><div><br></div><div>-Chris</div><div><br></div><br></font></span></div></blockquote></div><br></div>
</div></div></blockquote></div></div></div><br></div>
<br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailma<wbr>n/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div>
______________________________<wbr>_________________<br>swift-evolution mailing list<br><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br></div></blockquote></div><br></div></div></div></blockquote></div><br></div>
</div></blockquote><blockquote type="cite"><div><span>_______________________________________________</span><br><span>swift-evolution mailing list</span><br><span><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br></div></blockquote></body></html>