A couple things: * I think a C protocol / FFI for Arrow array/vectors would be better to have the same "shape" as an assembled array. Note that the C structs here have very nearly the same "shape" as the data structure representing a C++ Array object [1]. The disassembly and reassembly here is substantially simpler than the IPC protocol. A recursive structure in Flatbuffers would make RecordBatch messages much larger, so the flattened / disassembled representation we use for serialized record batches is the correct one
* The "formal" C protocol having the "assembled" shape means that many minimal Arrow users won't have to implement any separate data structures. They can just use the C struct directly or a slightly wrapped version thereof with some convenience functions. * I think that requiring building a Flatbuffer for minimal use cases (e.g. communicating simple record batches with primitive types) passes on implementation burden to minimal users. I think the mantra of the C protocol should be the following: * Users of the protocol have to write little to no code to use it. For example, populating an INT32 array should require only a few lines of code * The data structure in the protocol is suitable as an in-memory data structure for recursive assembly of nested structures I think that having a string miniformat or a pre-parsed type struct with enum values (along the lines of what Antoine is describing above) places less burden on downstream users. [1]: https://github.com/apache/arrow/blob/master/cpp/src/arrow/array.h#L203 On Mon, Sep 30, 2019 at 4:08 PM Antoine Pitrou <anto...@python.org> wrote: > > > FlatCC is still a dependency, with generated files etc. > Perhaps you want to evaluate FlatCC on a schema-like example and see > what the generated code and compile instructions look like? > > I'll point out again that the format string in my proposal uses an > extremely simple mini-format, that should be parsable very easily by any > developer, even in raw C: > https://github.com/apache/arrow/blob/3806fa9ba3ddf95f0d09b865071bf19c5e912756/docs/source/format/CProtocol.rst#data-type-description----format-strings > > The parent-child structure in the schema is represented as-is in the > ArrowArray parent-child relationship, so it doesn't need any encoding. > Using Flatbuffers for an enum-like field + (at most) a couple parameters > sounds overkill. > > Another possibility would be to replace the format string with > pre-parsed fields, for example: > > int32_t type; > int32_t subtype; // type-dependent (e.g. unit for temporal types) > int32_t type_width; // for width-parametered types > const int8_t* child_ids; // for unions > const char* auxiliary_type_param; // e.g. timezone for timestamp > > The downside is that there are more fields to consider (including two > optional pointers). > > Regards > > Antoine. > > > Le 30/09/2019 à 22:48, Ben Kietzman a écrit : > > FlatCC seems germane: https://github.com/dvidelabs/flatcc > > > > It compiles flatbuffer schemas down to (idiomatic?) C > > > > Perhaps the schema and batch serialization problems should be solved by > > storing everything in the flatbuffer format. > > Then the results of running flatcc plus a few simple helpers can be checked > > in to provide an accessible C API. > > With respect to lifetime, Antoine has already done good work on specifying > > a move only contract which could probably be adapted. > > > > > > On Sun, Sep 29, 2019 at 2:44 PM Antoine Pitrou <anto...@python.org> wrote: > > > >> > >> One basic design point is to allow exchanging Arrow data with no > >> mandatory dependency (the exception is JSON and base64 if you want to > >> act on metadata - but that's highly optional, and those are extremely > >> widespread formats). I'm afraid that Flatbuffers may be a deterrent: > >> not only it introduces a library, but it requires the use of a compiler > >> to produce generated code. It also requires familiarizing with, well, > >> Flatbuffers :-) > >> > >> We can of course discuss this and feel it's not a problem. > >> > >> Regards > >> > >> Antoine. > >> > >> > >> Le 29/09/2019 à 19:47, Wes McKinney a écrit : > >>> There are two pieces of serialized data needed to communicate a record > >>> batch from one library to another > >>> > >>> * Serialized schema (i.e. what's in Schema.fbs) > >>> * Serialized "data header", i.e. the "RecordBatch" message in Message.fbs > >>> > >>> You _do_ need to use a Flatbuffers library to fully create these > >>> message types to interact with any existing record batch disassembly / > >>> reassembly. > >>> > >>> I think I'm most concerned about having a new way to serialize > >>> schemas. We already have JSON-based schema serialization for > >>> integration test purposes, so one possibility is to standardize that > >>> and make it a more formalized part of the project specification. > >>> > >>> As far as a C protocol, I don't see an especial downside to using the > >>> Flatbuffers schema to communicate types. > >>> > >>> Another thought is to not deviate from the flattened > >>> Flatbuffers-styled representation but to translate the Flatbuffers > >>> types into C types: namely a C struct-based version of the > >>> "RecordBatch" message. > >>> > >>> Independent of the means to communicate the two pieces of serialized > >>> information above (respectively: schemas and record batch field memory > >>> addresses and field lengths), having a C-based FFI where project can > >>> drop in a header file containing the ABI they are supposed to > >>> implement, that seems pretty reasonable to me. > >>> > >>> If we don't define a standardized in-memory FFI (whether it uses the > >>> Flatbuffers objects as inputs/outputs or not) then downstream project > >>> will devise their own, and that will cause issues long term. > >>> > >>> On Sun, Sep 29, 2019 at 2:59 AM Antoine Pitrou <anto...@python.org> > >> wrote: > >>>> > >>>> > >>>> Le 29/09/2019 à 06:10, Jacques Nadeau a écrit : > >>>>> * No dependency on Flatbuffers. > >>>>> * No buffer reassembly (data is already exposed in logical Arrow > >> format). > >>>>> * Zero-copy by design. > >>>>> * Easy to reimplement from scratch. > >>>>> > >>>>> I don't see how the flatbuffer pattern for data headers doesn't > >> accomplish > >>>>> all of these things. At its definition, is a very simple > >> representation of > >>>>> data that could be worked with independently of the flatbuffers > >> codebase. > >>>>> It was designed so systems could map directly into that memory without > >>>>> interacting with a flatbuffers library. > >>>>> > >>>>> Specifically the following three structures were designed to already > >> allow > >>>>> what I think this proposal is trying to recreate. All three are very > >> simple > >>>>> to construct in a direct, non-flatbuffer dependent read/write pattern. > >>>> > >>>> Are they? Personally, I wouldn't know how to do that. I don't know > >>>> which encoding Flatbuffers use, whether it's C ABI-compatible (how could > >>>> it be? if it's portable accross different platforms, then it's probably > >>>> not compatible with any particular platform's C ABI, or only as a > >>>> conincidence), how I'm supposed to make use of the "offset" field, or > >>>> what the lifetime / ownership of all this data is. > >>>> > >>>> I may be missing something, but if the answer is that it's easy to > >>>> reimplement Flatbuffers' encoding without relying on the Flatbuffers > >>>> project's source code, I'm a bit skeptical. > >>>> > >>>> Regards > >>>> > >>>> Antoine. > >>>> > >>>> > >>>>> > >>>>> struct FieldNode { > >>>>> length: long; > >>>>> null_count: long; > >>>>> } > >>>>> > >>>>> struct Buffer { > >>>>> offset: long; > >>>>> length: long; > >>>>> } > >>>>> > >>>>> table RecordBatch { > >>>>> length: long; > >>>>> nodes: [FieldNode]; > >>>>> buffers: [Buffer]; > >>>>> } > >>>>> > >>>>> On Sat, Sep 28, 2019 at 9:02 PM Jacques Nadeau <jacq...@apache.org> > >> wrote: > >>>>> > >>>>>> I'm not clear on why we need to introduce something beyond what > >>>>>> flatbuffers already provides. Can someone explain that to me? I'm not > >>>>>> really a fan of introducing a second representation of the same data > >> (as I > >>>>>> understand it). > >>>>>> > >>>>>> On Thu, Sep 19, 2019 at 1:15 PM Wes McKinney <wesmck...@gmail.com> > >> wrote: > >>>>>> > >>>>>>> This is helpful, I will leave some comments on the proposal when I > >>>>>>> can, sometime in the next week. > >>>>>>> > >>>>>>> I agree that it would likely be opening a can of worms to create a > >>>>>>> semantic mapping between a generalized type grammar and Arrow's > >>>>>>> specific logical types defined in Schema.fbs. If we go down this > >>>>>>> route, we should probably utilize the simplest possible grammar that > >>>>>>> is capable of encoding the Type Flatbuffers union values. > >>>>>>> > >>>>>>> On Thu, Sep 19, 2019 at 2:49 PM Antoine Pitrou <solip...@pitrou.net> > >>>>>>> wrote: > >>>>>>>> > >>>>>>>> > >>>>>>>> I've posted a draft specification PR here, this should help orient > >> the > >>>>>>>> discussion a bit: > >>>>>>>> https://github.com/apache/arrow/pull/5442 > >>>>>>>> > >>>>>>>> Regards > >>>>>>>> > >>>>>>>> Antoine. > >>>>>>>> > >>>>>>>> > >>>>>>>> > >>>>>>>> On Wed, 18 Sep 2019 19:52:38 +0200 > >>>>>>>> Antoine Pitrou <anto...@python.org> wrote: > >>>>>>>>> Hello, > >>>>>>>>> > >>>>>>>>> One thing that was discussed in the sync call is the ability to > >> easily > >>>>>>>>> pass arrays at runtime between Arrow implementations or > >>>>>>> Arrow-supporting > >>>>>>>>> libraries in the same process, without bearing the cost of linking > >> to > >>>>>>>>> e.g. the C++ Arrow library. > >>>>>>>>> > >>>>>>>>> (for example: "Duckdb wants to provide an option to return Arrow > >> data > >>>>>>> of > >>>>>>>>> result sets, but they don't like having Arrow as a dependency") > >>>>>>>>> > >>>>>>>>> One possibility would be to define a C-level protocol similar in > >>>>>>> spirit > >>>>>>>>> to the Python buffer protocol, which some people may be familiar > >> with > >>>>>>> (*). > >>>>>>>>> > >>>>>>>>> The basic idea is to define a simple C struct, which is ABI-stable > >> and > >>>>>>>>> describes an Arrow away adequately. The struct can be > >>>>>>> stack-allocated. > >>>>>>>>> Its definition can also be copied in another project (or interfaced > >>>>>>> with > >>>>>>>>> using a C FFI layer, depending on the language). > >>>>>>>>> > >>>>>>>>> There is no formal proposal, this message is meant to stir the > >>>>>>> discussion. > >>>>>>>>> > >>>>>>>>> Issues to work out: > >>>>>>>>> > >>>>>>>>> * Memory lifetime issues: where Python simply associates the > >> Py_buffer > >>>>>>>>> with a PyObject owner (a garbage-collected Python object), we need > >>>>>>>>> another means to control lifetime of pointed areas. One simple > >>>>>>>>> possibility is to include a destructor function pointer in the > >>>>>>> protocol > >>>>>>>>> struct. > >>>>>>>>> > >>>>>>>>> * Arrow type representation. We probably need some kind of > >> "format" > >>>>>>>>> mini-language to represent Arrow types, so that a type can be > >>>>>>> described > >>>>>>>>> using a `const char*`. Ideally, primitives types at least should > >> be > >>>>>>>>> trivially parsable. We may take inspiration from Python here > >>>>>>> (`struct` > >>>>>>>>> module format characters, PEP 3118 format additions). > >>>>>>>>> > >>>>>>>>> Example C struct definition (not a formal proposal!): > >>>>>>>>> > >>>>>>>>> struct ArrowBuffer { > >>>>>>>>> void* data; > >>>>>>>>> int64_t nbytes; > >>>>>>>>> // Called by the consumer when it doesn't need the buffer anymore > >>>>>>>>> void (*release)(struct ArrowBuffer*); > >>>>>>>>> // Opaque user data (for e.g. the release callback) > >>>>>>>>> void* user_data; > >>>>>>>>> }; > >>>>>>>>> > >>>>>>>>> struct ArrowArray { > >>>>>>>>> // Type description > >>>>>>>>> const char* format; > >>>>>>>>> // Data description > >>>>>>>>> int64_t length; > >>>>>>>>> int64_t null_count; > >>>>>>>>> int64_t n_buffers; > >>>>>>>>> // Note: this pointers are probably owned by the ArrowArray > >> struct > >>>>>>>>> // and will be released and free()ed by the release callback. > >>>>>>>>> struct BufferDescriptor* buffers; > >>>>>>>>> struct ArrowDescriptor* dictionary; > >>>>>>>>> // Called by the consumer when it doesn't need the array anymore > >>>>>>>>> void (*release)(struct ArrowArrayDescriptor*); > >>>>>>>>> // Opaque user data (for e.g. the release callback) > >>>>>>>>> void* user_data; > >>>>>>>>> }; > >>>>>>>>> > >>>>>>>>> Thoughts? > >>>>>>>>> > >>>>>>>>> (*) For the record, the reference for the Python buffer protocol: > >>>>>>>>> https://docs.python.org/3/c-api/buffer.html#buffer-structure > >>>>>>>>> and its C struct definition: > >>>>>>>>> > >>>>>>> > >> https://github.com/python/cpython/blob/v3.7.4/Include/object.h#L181-L195 > >>>>>>>>> > >>>>>>>>> Regards > >>>>>>>>> > >>>>>>>>> Antoine. > >>>>>>>>> > >>>>>>>> > >>>>>>>> > >>>>>>>> > >>>>>>> > >>>>>> > >>>>> > >> > >