-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Change immutable class to struct #8226
Conversation
Can't |
@r00ster91 not sure, because of |
Is this for performance? |
Yes, this is for performance, see: https://crystal-lang.org/reference/guides/performance.html#use-structs-when-possible. @j8r I think it would work to make it a struct. |
I thought it might be. I was just hoping to verify there was a performance benefit if that is what the change is for. |
Yes, please provide benchmarks. Group is kind of huge, maybe copying that is slow. |
I can provide benchmarks, there is likely little difference. It's not only about performance, but also to mark the immutability of the object. Usually, I'm more confident when I see structs because there is little to no side effects involved, with often better performance/memory usage of course. |
Hmm what about
|
@r00ster91 |
4fc2583
to
c1f649f
Compare
Benchmark: def method(s)
s
end
Benchmark.ips do |x|
x.report("Group class") do
g = System::Group.find_by name: "root"
method g
end
x.report("Group struct") do
g = System::GroupStruct.find_by name: "root"
method g
end
end
Benchmark.ips do |x|
x.report("User class") do
u = System::User.find_by name: "root"
method u
end
x.report("User struct") do
u = System::UserStruct.find_by name: "root"
method u
end
end Result:
As expected, not much difference. |
Group Struct is slower? |
|
Being immutable isnt the only matter. Transforming a small class into a struct can improve performance, but a large class with many ivars can involve lots of memcpy instead of just passing a pointer. Your benchmark doesnt test copying because the method call will be inlined by LLVM. Making Process::Status a struct makes perfect sense: it merely wraps a 32-bit integer, thus will be pased inside a register (no memcpy) and it's immutable. Making System::User doesnt make sense: 6 string structs, most nilable. The class is huge. Passing it by value will require lots of memcpy. |
I have to add |
I understand performance is not the only reason why to make this change. I just thought it would be the way. |
Updated benchmark: @[NoInline]
def another(s)
s
end
@[NoInline]
def method(s)
999.times { another s }
end Results:
|
c1f649f
to
75342c0
Compare
As I expected @r00ster91 , changing |
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.
changing Process::Status
seems fine
changing OAuth::RequestToken
is unlikely to have any effect, is not a type that is used a lot I think.
But I would not change Group
or User
as expressed by others.
As measured @bcardiff , there is no difference. |
Any update? |
e78670c
to
c1fa9cf
Compare
Is it possible to move forward? I noticed other objects that can be changed from class to struct.
|
66285ed
to
7747aba
Compare
Sorry, but this is not needed. This will cause breaking changes without real benefits. |
For |
For But generally, how many http servers are created per app? One? It doesn't make a difference if it's a class or a struct. And a class is more versatile because you can inherit from it. |
Yes I agree for |
And |
7747aba
to
f80b88b
Compare
The default for every instance type in Crystal API's should be But just refactoring classes to structs because they're immutable types doesn't make sense. Especially when they're not inherently immutable but just happen to have no mutable state in the current implementation. Classes can be immutable. That's perfectly fine and there's no inherent benefit to converting them to structs. |
|
Here I don't know what do you mean by "inherently immutable", there is no golden rule for that. There are obvious simp'e examples, but we can just look at the implementation and the public API to know if an object is mutable or not – and switching between class and struct will be transparent (except for inheritance). The main questions are: is it mutable, and is inheritance needed? |
Why not for the other |
f80b88b
to
96035e7
Compare
By inherently immutable I mean value objects and similar concepts.
This only covers the current implementation and API. Later changes adding mutable state would mean a break change from struct to class. And so does the current change from class to struct. Since there's no real benefit from doing that in the first place, we shouldn't even bother having this discussion.
It's not transparent but a breaking change affecting inheritance, reopening and instance behaviour.
Because they're behavioural models, not value objects. |
I guess I can use memoization and a hash table to avoid creating a new OAuth2 client for each request. |
Extracting Oauth connections to a pool or whatnot may very well make sense from an efficiency standpoint (new connections are not free), but is this PR really a good place to do that kind of change? Wouldn't it make more sense to drive that kind of change because it is good and necessary rather than doing it in some quest for purity? |
I was only talking about my use case, on an efficiency point a view – not changes to make here. |
In fact, in my app, a proxy, a lot of requests are processed – I was investigating ways to improve processing time. |
(My 3 last comments are not related to this PR, I explain the rationale from my point a view for those wondering why) |
96035e7
to
869c553
Compare
@j8r Please, change this PR title and description accordingly to your latests changes. |
Just for the sake of argument: |
Is to OK to merge now? Only |
I noticed, after looking at the implementations, some places where classes can be changed to structs, because the objects are immutable.