-
Notifications
You must be signed in to change notification settings - Fork 192
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
Make builders optional #483
Conversation
Reduces build time by 22%
Yeah I think I'd rather go for seeing if we can unify the builders and native structs instead. Have you also checked the build time reduction when merely omitting trivial setters (removing everything except those touching lifetimes/arrays)? That might give a better indication what we could achieve in #441, bar having two types instead of one. In particular I'm referring to the: let foo = Foo { f, .. Default::default() }.with_bar_slice(&bar) Where |
I'd like to see an option that has the builder methods fused onto the native structs. The methods are helpful but the builder structs themselves are not. I did an experiment in #286 to try out two scenarios: (1) remove the builders entirely and (2) remove only the lifetime from the builders. The lifetimes in the builders are what causes them to compile so slowly, and add little value since However, if we're now entertaining the idea of making an option to remove the builders, we should move the builder methods to the native structs. The compile time is only increased slightly. |
What is that in relative terms, for a clean build? Hard to generalize an absolute change between environments.
To the contrary, the lifetimes are the most valuable part. The trick is that you should almost never have to call The plan we discussed in #441 was to move the lifetimes, along with the setter methods, into the Vulkan structs/calls themselves. If that still leaves a lot of buildtime on the table, that's unfortunate... dropping lifetimes entirely will certainly lead to more people shooting themselves in the foot, but perhaps there's a finite amount of buildtime that's worth trading off against. |
"Almost never" is not the same as "never". The biggest quality-of-life improvement of the builders is when you need pass in a slice of something and this is the use case that requires #441 doesn't work for slices, which makes it a non-starter, IMO. The following addresses both the compilation slowness and footgun issues, while still keeping the quality-of-life improvements:
I apologize if my tone comes across as anything but neutral, but I'm frustrated by the fact that this continues to pop up every so often and there has been no willingness to accept the fact that erasing a lifetime is |
Then I will disregard the following paragraph in which you do so :P
The conclusion of the discussion in #441 included:
If we then lifetime-qualify the Vulkan structs themselves then there's no need to erase lifetimes at all. If we don't, then that's a big step backwards in static safety checking compared to the currently recommended pattern, which is a shame. I think we should prototype this and get hard numbers for the impact. We've been waiting for @MaikKlein's generator rewrite before diving into that, though that seems to be in limbo at the moment. |
That's what I'm suggesting. But, I don't think it's a good idea to treat the arrays and non-array pointers differently though, and you can't embed a slice into the native struct for the reasons you stated. However, maybe we could add #[repr(C)]
pub struct DeviceCreateInfo<'a> {
pub s_type: StructureType,
pub p_next: *const c_void,
pub flags: DeviceCreateFlags,
pub queue_create_info_count: u32,
pub p_queue_create_infos: *const DeviceQueueCreateInfo,
pub enabled_layer_count: u32,
pub pp_enabled_layer_names: *const *const c_char,
pub enabled_extension_count: u32,
pub pp_enabled_extension_names: *const *const c_char,
pub p_enabled_features: *const PhysicalDeviceFeatures,
pub _phantom: PhantomData<&'a ()>,
}
impl<'a> DeviceCreateInfo<'a> {
pub fn queue_create_infos(mut self, queue_create_infos: &'a [DeviceQueueCreateInfo]) -> Self {
self.inner.queue_create_info_count = queue_create_infos.len() as _;
self.inner.p_queue_create_infos = queue_create_infos.as_ptr();
self
}
pub fn enabled_features(mut self, enabled_features: &'a PhysicalDeviceFeatures) -> Self {
self.inner.p_enabled_features = enabled_features;
self
}
} |
I like the consistency of that! Probably saves some effort rewriting the types, too. |
The other thing to consider is whether or not these should be "pure" builders (e.g. take let mut create_info = DeviceCreateInfo::default();
create_info.some_hypothetical_field = 42;
create_info.set_queue_create_infos(infos); |
My habit is to omit the I think taking and returning
By comparison, taking |
I tried the same thing as #286 to not generate the builders at all (and replace the few uses in extensions with a I guess we'll pick up most of that time again when moving the lifetimes and useful builder functions directly into the native structs (in whatever way discussed above), making me curious if we're leaving build-time performance on the table elsewhere. Fwiw removing all manual modules ( |
Perhaps llvm-lines can help us target what's next. Here's the top output from it:
Dropping all the debug implementations nets us another ~20-25% - together with the full builder removal it's down from 10s to just about 5s on my machine. Perhaps a good idea to move that all behind a |
i.e. #482? |
Gah, I should learn to remember all PRs floating around. Neat! |
#602 drafts folding things together. Currently setters are all by-value just because that was the easiest place to start. |
Closing in favor of #602. |
Reduces build time by ~22% in my environment.
I don't know if we should actually merge this as-is, since builders are such an important correctness helper. However, it's strong evidence that reducing the amount of builder code, e.g. as discussed in #441, could have a large benefit.