-
Notifications
You must be signed in to change notification settings - Fork 2
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
try out table-driven Visitor::element
and Visitor::attribute
to reduce code size
#5
Comments
I just realized that on stable Rust, it's currently impossible to calculate offsets within structures from a
It looks like folks are working on stabilizing this stuff; I hope that trade-off doesn't last long. |
This doesn't work yet. It has memory errors because it's not actually initializing the flattened fields before calling assume_init(). I'm just using this to get a very very rough estimate of the code size of this approach. tl;dr: * the .text is getting noticeably smaller * the serialization path is starting to show up now, in spite of fewer types actually getting serialized. This is good news because I can put serialization into the same table at a cost of one pointer per field. * the total file size isn't changing! :-( It looks like the non-.text sections are basically replacing the .text section's size. I think a lot of it is symbol names. Some of it was worse before I added #[inline] in a couple spots. This is currently defining a visitor for each type and then delegating to the vtable. It's not getting the full benefit that way: each of these methods still takes up some room, and (perhaps more importantly) still has to be written to the symbol table. I think the next step is to eliminate that delegation. (Or, you know, make it actually work. But whatever.)
There's another problem I didn't anticipate: some schemas have cycles. Here's a trivial one (a type that refers directly back to itself): #[derive(PartialEq, Debug, Serialize, Deserialize)]
#[static_xml(prefix = "tt", namespace = "tt: http://www.onvif.org/ver10/schema")]
pub struct Transport {
// Defines the network protocol for streaming, either UDP=RTP/UDP,
// RTSP=RTP/RTSP/TCP or HTTP=RTP/RTSP/HTTP/TCP
#[static_xml(prefix = "tt", rename = "Protocol")]
pub protocol: TransportProtocol,
// Optional element to describe further tunnel options. This element is
// normally not needed
#[static_xml(prefix = "tt", rename = "Tunnel")]
pub tunnel: Vec<Transport>,
} The type vtable for Rust understandably doesn't let me generate struct Foo(&'static Foo);
const A: Foo = Foo(&B);
const B: Foo = Foo(&A); Similarly, producing the vtable from a
I guess I can build these in a lazy cell, have them to reference the lazy cell itself (not using its Or something like building a (mutex-guarded) hash map keyed by |
I think this might be a dead end. :-( The problem is that I don't see a way to generate the vtables at compile-time (for the reasons described above), and the code to generate them more than cancels out the gains of the table-driven approach.
I can improve this a little bit by using the struct for serialization as well as deserialization, but when it gets down to it, those initialize closures are just huge. |
Immediately after that I realized that I can actually generate the vtable at construction time; I just need to basically launder the constant through a function. (It made sense to me at first that Rust couldn't have cycles in constant references. But then I realized functions allow this in basically every language. So now I'm wondering if the limitation is actually necessary, but I have a workaround anyway.) 520af8a implements this approach, and now total file size is ticking downward:
so basically this is smaller on nightly; and I can use a feature flag to make it work on stable at the cost of bloat. This is sounding promising. Major caveat: this code doesn't actually work yet. I'm going to address that next...but I don't expect it's broken in a way that totally invalidates the numbers above. And those numbers can be improved; I can shrink the tables themselves, and I can use the tables to eliminate the serialization bloat also. |
Grr. I'm having a tough time with this. Making it (mostly) work did increase binary size; there were some missing bits that apparently cause significant bloat. What's more is that I'm having a tough time understanding what the bloat is. It's not in the
The obvious question is: are the tables taking up a lot of space? It's a little awkward to measure how big they are, so the easiest thing I can do is make them even bigger and measure how much the file grows. Here I've added 256 bytes to the vtable for each type and 32 bytes to the vtable for each field:
so the tables basically are all in I think it's more about total numbers of symbols increasing size of the
I wish Rust had associated statics; then I wouldn't need all the I also wish I could get rid of all these Maybe it's worth combining I suppose I could drop those symbols entirely if I didn't support
but I'd like to make these types feel normal. There's also some bloat with I'm tempted to actually try changing the |
I played with this a bit, and it's probably a dead end. |
Try out the approach to reduce the per-type overhead of these methods described in the README:
Currently
static-xml-derive
writes explicit generated code.E.g., with this type:
The
Deserialize
macro produces code roughly as follows:I believe this is close to the minimal size with this approach. Next I'd like to experiment with a different approach in which the
Visitor
impl is replaced with a table that holds the offset withinFooVisitor
of each field, and a pointer to anelement
function. The generated code would useunsafe
, but soundness only has to be proved once in the generator, and this seems worthwhile if it can achieve significant code size reduction.The text was updated successfully, but these errors were encountered: