Skip to content

perf: load TestSet results as lightweight AttrDicts - #88

Open
mmghannam wants to merge 3 commits into
scipopt:mainfrom
mmghannam:perf/lightweight-result-loading
Open

perf: load TestSet results as lightweight AttrDicts#88
mmghannam wants to merge 3 commits into
scipopt:mainfrom
mmghannam:perf/lightweight-result-loading

Conversation

@mmghannam

Copy link
Copy Markdown
Member

This avoids parsing each data entry from elastic search as a python object, and just keeps as it the data's original form: a dictionary.

Measured performance gain on six 328-instance runs:
single run view 1.65s -> 0.41s
comparison page 7.74s -> 1.50s
evaluation 10.8s -> 2.70s

…uments

Loading a TestSet's results dominated the comparison and evaluation views.
Result documents are very wide (~1.7k fields each, one per IPET metric), and
elasticsearch-dsl hydrating every field of every doc into Document/AttrDict
objects is ~6x slower than reading the raw _source dicts (measured 1.14s vs
0.19s for a 328-instance run). The same results were also hydrated repeatedly:
once per run in the comparison page, then again per run in the evaluation.

load_results() now scans the raw _source dicts and wraps each in a small
ResultHit(AttrDict) that carries meta.id as a real attribute (so it stays out
of to_dict(), which feeds the IPET evaluation). All downstream code only needs
attribute access, item access and to_dict(), which AttrDict already provides.
delete_all_results() switches to a single delete-by-query since the loaded
values are no longer Documents with .delete().

Output is byte-identical. Measured on six 328-instance runs:
  single run view      1.65s -> 0.41s
  comparison page      7.74s -> 1.50s
  evaluation           10.8s -> 2.70s

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPAST8EPQBUNTEB7ucBEXH
Comment thread rubberband/models/__init__.py Outdated

def __init__(self, doc):
super().__init__(doc["_source"])
self.meta = AttrDict({"id": doc["_id"]})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be executed or work and meta is None:

01-08-2026 10:35:58 - 359 ERROR tornado.application Uncaught exception GET /result/XiVLoZ8B-ExwBovct_Oy?compare=ayVEoZ8B-ExwBovccvAH (127.0.0.1)
HTTPServerRequest(protocol='http', host='rubberband.zib.de', method='GET', uri='/result/XiVLoZ8B-ExwBovct_Oy?compare=ayVEoZ8B-ExwBovccvAH', version='HTTP/1.1', remote_ip='127.0.0.1')
Traceback (most recent call last):
  File "/usr/local/lib/python3.13/site-packages/tornado/web.py", line 1878, in _execute
    result = method(*self.path_args, **self.path_kwargs)
  File "/rubberband/rubberband/handlers/fe/result.py", line 89, in get
    self.render(
    ~~~~~~~~~~~^
        "result_view.html",
        ^^^^^^^^^^^^^^^^^^^
    ...<9 lines>...
        evals=IPET_EVALUATIONS,
        ^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/usr/local/lib/python3.13/site-packages/tornado/web.py", line 1025, in render
    html = self.render_string(template_name, **kwargs)
  File "/usr/local/lib/python3.13/site-packages/tornado/web.py", line 1174, in render_string
    return t.generate(**namespace)
           ~~~~~~~~~~^^^^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/tornado/template.py", line 362, in generate
    return execute()
  File "result_view_html.generated.py", line 237, in _tt_execute
    href="/instance/{rid}?compare={compare}".format(rid=r.meta.id, compare=','.join([c.meta.id for c in compare]))  # results/details_table.html:32 (via results/res_view_details.html:37, result_view.html:65, wide.html:9, base.html:80)
                                                        ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'id'
01-08-2026 10:35:58 - 369 ERROR tornado.access  500 GET /result/XiVLoZ8B-ExwBovct_Oy?compare=ayVEoZ8B-ExwBovccvAH (127.0.0.1) 2599.97ms

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Stefan! seems that AttrDict.__setattr__ is overloaded and overrides the value of meta that is in the result. I changed it to just calling the default __setattr__ for any object directly.

mmghannam and others added 2 commits August 2, 2026 08:56
…ults

ResultHit declared `meta = None` at class level so that AttrDict.__setattr__
would store the assigned meta as a real attribute. Its condition is
`name in self._d_ or not hasattr(self.__class__, name)`, so that only holds
while no result document has a field named "meta": when one does, the first
clause wins, meta is stored as a source field, and hit.meta then resolves to
the class attribute None. That 500'd the comparison view (details_table.html
calls r.meta.id), overwrote the document's own meta value, and leaked
{"id": ...} into to_dict(), which feeds the IPET evaluation. Setting meta with
object.__setattr__ bypasses the condition entirely.

get_data() also built its per-instance dicts straight from ResultHit.to_dict().
Unlike Document.to_dict(), AttrDict.to_dict() returns the underlying dict
rather than a copy, so the keys get_data() adds (instance_id, TimeLimit,
GitHash, RubberbandId, ...) were written into the cached results instead of a
throwaway. Copy before mutating.

Reported by @svigerske on scipopt#88.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AkYsUSgc6FaPJxHtDUFzvW
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants