metadata access counter; closes #3472 - #3475
Conversation
c9090a1 to
d061de1
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3475 +/- ##
==========================================
+ Coverage 91.67% 91.68% +0.01%
==========================================
Files 38 38
Lines 32188 32232 +44
Branches 5151 5153 +2
==========================================
+ Hits 29509 29553 +44
Misses 2346 2346
Partials 333 333
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
nspope
left a comment
There was a problem hiding this comment.
Looks fine by me, some minor suggestions
| def metadata(self): | ||
| if self._metadata_access_counter is not None: | ||
| self._metadata_access_counter += 1 | ||
| if self._metadata_access_counter > 20: |
There was a problem hiding this comment.
Rather than hardcoding 20 in multiple places, how about defining METADATA_WARNING_COUNT in __init__.py alongside the other constants, and importing it for use here (and in the tests)?
| # should warn after 20 times and no more after that | ||
| md = t.metadata | ||
| for _ in range(19): | ||
| print(_, t._metadata_access_counter) |
| def metadata(self): | ||
| if self._metadata_access_counter is not None: | ||
| # can't set things directly on this immutable class | ||
| builtins.object.__setattr__( |
There was a problem hiding this comment.
Do you need builtins here? object exists without importing, so I think you can just do object.__setattr__ here (and elsewhere) and remove the import. (That's done elsewhere, for example with the _initialised member of the same class)
There was a problem hiding this comment.
Hm: I did it that way because of this bit. Looks like you're right, though, don't need it.
|
Here's one thing which isn't wrong per se but is a bit messy: checking equality may raise the counters of the involved tree sequences/tables, to a degree that varies depending on what's compared. For example, import msprime
import tskit
ts = msprime.sim_ancestry(3, sequence_length=1, random_seed=1024)
tables = ts.dump_tables()
tables.metadata_schema = tskit.MetadataSchema.permissive_json()
tables.metadata = {"a": "bcde", "x": [1, 2, 3, 4]}
ts = tables.tree_sequence()
# mutable vs mutable, same metadata (+0, +0)
t1, t2 = ts.dump_tables(), ts.dump_tables()
t1.assert_equals(t2)
print(t1._metadata_access_counter, t2._metadata_access_counter) # (0, 0)
# mutable vs mutable, diff metadata (+2, +2)
t3, t4 = ts.dump_tables(), ts.dump_tables()
t4.metadata = {"a": "zzz"}
try:
t3.assert_equals(t4)
except AssertionError:
pass
print(t3._metadata_access_counter, t4._metadata_access_counter) # (2, 2)
# immutable vs immutable, same metadata (+1, +1)
t5, t6 = ts.tables, ts.dump_tables().tree_sequence().tables
t5.assert_equals(t6)
print(t5._metadata_access_counter, t6._metadata_access_counter) # (1, 1)I'm not sure there's an easy way to avoid this though (and it's just cosmetic, would just add some unavoidable noise when comparing things in a loop for instance) |
|
Good point about comparisons. I don't think I'm worried about it, though? Thanks for the input! Will get on it. |
jeromekelleher
left a comment
There was a problem hiding this comment.
I agree this is a good least-bad option here given the dependence on the return value being a mutable copy in downstream code. I think we should make the interface "official" though and make it flexible, as there will be cases where this is really annoying and we want to turn it off.
|
I've set So, if we set it to 1Mb, then here we're running 12 tests that would each take 9s (in parallel, but still). However, if 200Kb is too small then we should up that. However, I think we're taking 2s per decode, so we're not warning until the user has spent 40s decoding that could be cut to 2s, so I think this is okay. Note that I'm using the Still TODO: documentation. |
|
Docs done. This should be ready to go. |
|
Yep - I've had a careful read through, and I think we're good. |
nspope
left a comment
There was a problem hiding this comment.
Docs look good to me, a couple minor nits
|
|
||
| Because of this, tskit will produce a Warning if the top-level metadata | ||
| is large (greater than 100Kb) and is accessed many times (more than 20 times). | ||
| This behavior can be changed, using |
There was a problem hiding this comment.
| This behavior can be changed, using | |
| This behavior can be changed, by setting |
jeromekelleher
left a comment
There was a problem hiding this comment.
I think this is a good approach, but the implementation is a bit complicated. Implementing a cheap metadata_size property here is straightforward, and avoids jumping through hoops.
|
|
||
| @property | ||
| def metadata(self): | ||
| if self._metadata_access_counter is not None: |
There was a problem hiding this comment.
Then do
if self._ll_tables.metadata_size > METADATA_ACCESS_WARNING_SIZE:
self._metadata_access_counter += 1
This is more efficient as it avoids creating another copy of large metadata at instantiation time, and it's more straightforward semantically as you can set the thresholds any time you want.
|
Done! It is certainly cleaner. And hah now I see you only suggested adding |
|
Okay, this is I think finally ready to go! |
jeromekelleher
left a comment
There was a problem hiding this comment.
Happy to merge after the typo is fixed/variable is renamed.
| #: A warning will be thrown if top-level metadata is larger than "size" bytes and is | ||
| #: accessed more that "threshhold" times. To disable, set threshhold to a large number, | ||
| #: for instance: ``tskit.METADATA_ACCESS_WARNING_THRESHOLD = 2**32``. | ||
| METADATA_ACCESS_WARNING_THRESHHOLD = 20 |
There was a problem hiding this comment.
Sorry, I missed this the last time. I think we should change this to METADATA_ACCESS_WARNING_COUNT for symmetry with METADATA_ACCESS_WARNING_SIZE (they are both thresholds). This is backtracking a bit on what I said before, I know.
In any case, there's a typo so we'll need to change it (double "H" in threshold)
and add metadata_size property
e7fcffe to
78d5026
Compare
|
Done! |




As lengthily discussed in #3472, if top-level metadata is large then doing
ts.metadataa lot of times can be verrrrry slow, because of both the decoding and making a copy of the data (in roughly equal measure). Seeing no straightforward way around this, I'm proposing this stopgap: if someone doesTreeSequence.metadataorTableCollection.metadata20 times then they get a Warning suggesting they do something else. The warning only occurs once.