Discussion/Proposal: StreamCore to Stream.core
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
调研方向
Start by reading the Stream and StreamCore design described in the proposal, especially the current core methods and data structures; the issue does not name specific files or tests. The proposals are broad architectural alternatives still seeking discussion, so there is no defined implementation or completion criterion yet.
由索引模型根据 Issue 内容生成。
描述
I've been thinking for a bit about some Stream/StreamCore refactors that I think will improve music21's speed and improve typing and flexibility for the next decade, and wanted to open up for discussion.
A Stream is basically a sorted list of notes, other streams, and other Music21Objects -- functioning as a mutable sequence. To make it all work well, there are a bunch of "core" calls such as coreInsert, coreAppend, and some hidden data structures like _elements, _endElements, _offsetDict, etc. For small Streams (up to about 1000 objects) this is all fast enough that the fact that some of our worst case scenarios are O(n^2) doesn't matter.
For larger streams, we have the tree.trees.ElementTree data structure, but almost no one uses it, because it doesn't function much like a Stream, it's underdocumented, and below about 1000 objects in the stream it is so much slower than a normal stream, I wouldn't ever use it.
Furthermore, because of the way StreamCore basically functions as a Mixin class for Stream, there are a lot of problems with typing and subclassing that are not easy to deal with. So some proposals:
Proposal 1:
Change the ontology/hierarchy so core is an attribute of Stream
currently we have a taxonomy: Stream IS-A StreamCore IS-A Music21Object
core becomes a meronomy: Stream IS-A Music21Object directly. Stream HAS-A .core which is StreamCore.
Renaming:
methods like self.coreInsert() become self.core.insert()
What it slows down: self.core.func(), etc. has to dereference .core which costs about 1ns per call or a 10% slowdown. But for even the simplest functions that we're calling, 1ns per call is a tiny part of the call (like usually less than 1% if that), so it's not a real hinderance (we can gain back 1% easily in a lot of places; timing evidence at bottom)
Immediate gains: some private methods will be opened (endElements will lose its underscore, I'm pretty sure).
The boundaries between Stream (iter, getElementsByClass etc.) and core (offsetDict, etc.) become very clear. Optimizations can take place within core and testing becomes simpler.
One Optimization I'm already considering is storing data this was: [(sortTuple, element), ...] -- this is how music21j implements some things and it ends up working so much better with multiple sites than the current system.
Proposal 2:
Streams become always sorted. We remove isSorted, autoSort, etc. anything that works with a Stream that doesn't cross into the .core boundary can always be sure that correct sorting is taking place. (This gets back way more than 1% time I think with removing all the if not self.isSorted(): self.sort() routines).
The notion that sometimes we'd want to work with non-sorted streams was a 2006-2010 music21 idea that did not ever pan out.
Proposal 3
Here's the big eventual gain from 1 and 2 that makes me excited:
the current StreamCore--with elements in two lists with a second offsetDict data structure--does not need to be the only available .core -- when a stream gets over 1000 elements, we might have it automatically changed to use a different stream representation internally as .core (like "BigStreamCore" etc.)
We have one such data structure already: the Tree representation, which is slow for small trees but very fast for bigger structures.
Another one to consider using is sortedcontainers's SortedList. (I tried some experiments on always using them for every Stream, but it made the tests take over twice as long; but for big streams, they got huge improvements).
I've been mulling this over for more than a decade, but I think that it might finally be the right time to make changes of this size to make sure that Streams "just work" for another decade or more.
Thoughts?
Timing Evidence for .core
class A():
def i(self):
pass
class B():
def __init__(self):
self.a = A()
a_obj = A()
b_obj = B()
%timeit a_obj.i()
10.2 ns ± 0.0435 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)
%timeit b_obj.a.i()
11.9 ns ± 0.0588 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)
even making the function i() do any tiny thing makes the difference between the two methods be nearly a wash (2%) difference -- not enough to decide to go ahead or stop over (we're in the single ns range per call).
class A():
def i(self):
return randint(10, 20)
a_obj = A()
b_obj = B()
%timeit a_obj.i()
97.8 ns ± 0.266 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
%timeit b_obj.a.i()
101 ns ± 0.524 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
- 主要语言
- Python
- 星标
- 2.6k
- 派生
- 459
- 平均合并
- 1 天 7 小时
- 30 天内合并 PR
- 40
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
cuthbertLab/music21 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 72/100
cuthbertLab/music21#2084 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 75/100
cuthbertLab/music21#2082 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
cuthbertLab/music21#2074 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
cuthbertLab/music21#2055 · 3 条评论 ·
维护者通常 1 天内回复
-
Slur bezier-x/y from start element gets overwritten by stop element's values可能重新可做 @bingliscodes 于 9 天前认领,目前没有进行中的 PR。 未关闭
难度 2/5 1-3 小时 新手友好度 75/100
cuthbertLab/music21#2019 · 3 条评论 ·
维护者通常 1 天内回复
查看 cuthbertLab/music21 的全部 Issue
相似的 Issue
-
难度 1/5 1 小时以内 新手友好度 88/100
-
难度 2/5 1-3 小时 新手友好度 72/100
aws-samples/sample-per-model-bedrock#261 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 65/100
pwncollege/dojo#1188 ·
维护者通常 1 天内回复
-
bug launcher packaging
难度 1/5 1 小时以内 新手友好度 88/100
tenstorrent/tt-studio#1426 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 72/100
python-attrs/cattrs#796 ·
维护者通常 1 天内回复