-
Notifications
You must be signed in to change notification settings - Fork 184
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
allow caching of hashCode #760
Comments
Oops, sorry, I just noticed this duplicate issue: #210 In that issue you suggested "if you have a particular case in mind you could hack the .g.dart files yourself and try it." I'd like to try this to test out some ideas, but I'm not sure how. I can change the generated code in the .g.dart files, but in order for their effect to be tested, the project needs to be recompiled, which calls build_runner, which overwrites the .g.dart files with built_value's auto-generated code. Is there an option on webdev (https://dart.dev/tools/webdev) that tells it to skip the build_runner step so that .g.dart files will not be generated? |
I'm afraid I don't know how to do that with I'm not sure it makes sense to always cache hashCode. Do you think it would make sense that you opt-in like this?
|
Opt-in is okay, although I think opt-out is better. :) Why? First, it was a surprise to me that the behavior was different from built_collection. Second, it's a time/memory tradeoff right? If you have few objects then it's best to favor time (i.e, cache the I don't know how to parse annotations, but I implemented the version that unconditionally caches every result.writeln('int _hashCodeCached = null;');
result.writeln();
result.writeln('@override');
result.writeln('int get hashCode {');
result.writeln(' if (_hashCodeCached == null) {');
if (comparedFields.isEmpty) {
result.writeln('_hashCodeCached = ${name.hashCode};');
} else {
result.writeln(r'_hashCodeCached = $jf(');
result.writeln(r'$jc(' * comparedFields.length);
// Use a different seed for builders than for values, so they do not have
// identical hashCodes if the values are identical.
result.writeln(forBuilder ? '1, ' : '0, ');
result.write(comparedFields.map((field) => '${field.name}.hashCode').join('), '));
result.writeln('));');
}
result.writeln(' }');
result.writeln(' return _hashCodeCached;');
result.writeln('}');
result.writeln(); For a feature of my app where I'm click-dragging a selection box, where it fires an update on every mousemove (throttled to be 60 times/sec, so needs to take 1000/60 = 16.66 ms to be jank-free), this reduces the time spent calling hashCode on the various parts of the object tree from ~20 ms down to ~6 ms. |
Those are all good points. My worry is that when you have very few fields, an extra hidden field is surprising. For example if you have just one int, it's surprising that you need memory for two. It's also the case that not all 20ms->6ms is pretty convincing. I think the opt-in approach sounds good :D will aim to land that next time I do a batch of improvements. Thanks! |
I've profiled my application and found a huge amount of time is being spent in calls to the getter
hashCode
on built_value objects that have not changed since the last call tohashCode
.I debugged and was surprised to see that the generated implementation does not cache
hashCode
. (I recall though thathashCode
is cached for built_collection objects.) Is there a way to enable such caching?For example, one object with 9 fields has a generated
hashCode
implementation that looks like this:That's a lot of calls to
$jc
. And this is just one object near the bottom of a large object tree (all built_value and built_collection).hashCode
is being called on the whole thing quite frequently, so it's making things very janky.The text was updated successfully, but these errors were encountered: