-
Notifications
You must be signed in to change notification settings - Fork 33
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
Reduce possible overhead in Extends solver #538
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Looks potentially like a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a clear improvement. Thanks @s-and-witch.
Ok, I've checked CI error. It happens that previously extendSomeStruct
:: forall a e. (Extensible a, Extends a e)
=> e
-> SomeStruct a
-> SomeStruct a
extendSomeStruct e (SomeStruct @_ @es a) = SomeStruct (setNext a (e, getNext a)) Fails with such error:
To fix this error, we have to add equality constraint directly: extendSomeStruct
:: forall a e. (Extensible a, Extends a e ~ (() :: Constraint))
=> e
... I see several ways to deal with it:
type family ExtendsWith (a :: [Type] -> Type) (b :: Type) :: () where
ExtendsWith AccelerationStructureCreateInfoKHR OpaqueCaptureDescriptorDataCreateInfoEXT = '()
ExtendsWith AccelerationStructureCreateInfoKHR AccelerationStructureMotionInfoNV = '()
ExtendsWith AccelerationStructureCreateInfoNV OpaqueCaptureDescriptorDataCreateInfoEXT = '() And declare type Extends a b = ExtendsWith a b ~ '() That should be enough to hide the change from users, however, I want to know your opinion before implementing this. |
The definition of `Extendss` that involves pairs: type Extendss p xs where Extendss p '[] = () Extendss p (x : xs) = (Extends p x, Extendss p xs) Would produce a constraint that looks like this: `((), ((), ((), ...)))` That may result in a runtime overhead if GHC would be incapable to perform cross-module inlining here (it would be) The new `ReportUnsolved` type family ensures that `Extends p x` reduces to the constraint unit and drops it after that entirely. Hence, produced constraint would be just `()`. Much better! To ensure that having `Extends p x` and `Extendss p xs` implies `Extendss p (x : xs)`, all extension assotiations were moved to the `ExtendsWith` type family, and `Extends` now becomes just a constraint wrapper: type Extends p x = ExtendsWith p x ~ '() That should save backward compatibility.
SeqConstraintUnit
to reduce possible overheadReportUnsolved
to reduce possible overhead
@s-and-witch could you measure the compile time performance impact of this change? |
I think that if the change is relevant, it justifies the added complexity of your second proposed solution. |
Sure. I'm using environment from With my patch it takes
That's a lot! In current main, it takes just
|
Interesting. The change is negligible. Where does that leave us wrt "reducing possible overhead"? Do you think the possible performance benefits of this change would show up in another workload? |
Let's talk about statically observable changes using this showcase: https://play.haskell.org/saved/E3ELcj3Z The code of our interest is this, because it is an actual code taken from the
With previous logic,
The part that we want to avoid is this tuple allocation: With new type families, this function results in this code:
There is no more tuple allocation. However, matching on equality constraint was added: I can't confidently predict which result is better, I'm just feeling that evaluation of a single unit is better that lazy pair allocation. |
I mean runtime overhead for tuple allocation |
I see. It looks like a benign change user-facing modulo error messages, but also not trivial to judge (I'll try pointing a project of mine at this branch later). The generated code allocates less, that's good... Do you mind if I ask what prompted you investigating this/patch this on vulkan in particular? |
Another example require simulating cross-module inlining failure, e.g. when there is a function like With new API
And here is how it looks with old one:
|
There is no any major reason, I wanted to learn about vulkan by porting code a C++ guide into Haskell and found how |
ReportUnsolved
to reduce possible overhead
FWIW, I'm totally ok with rejecting this patch, because I expected a little patch that does a little speed improvement, however the patch grew a lot. |
The definition of
Extendss
that involves pairs:Would produce a constraint that looks like this:
((), ((), ((), ...)))
That may result in a runtime overhead if GHC would be incapable to perform cross-module inlining here (it would be)The new
ReportUnsolved
type family ensures thatExtends p x
reduces to the constraint unit and drops it after that entirely. Hence, produced constraint would be just()
. Much better!To ensure that having
Extends p x
andExtendss p xs
impliesExtendss p (x : xs)
, all extension associations were moved to theExtendsWith
type family, andExtends
now becomes just a constraint wrapper:That should save backward compatibility.
Here is a minimal example to show that it works as expected: https://play.haskell.org/saved/3UGbL6ef
The change should not affect any user, this is just an internal optimization.