This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
标题: sched.py: speedup cancel() method
类型: performance Stage: patch review
Components: Library (Lib) Versions: Python 3.9
process
状态: open Resolution:
Dependencies: 后续:
分配给: serhiy.storchaka 抄送列表: giampaolo.rodola, pitrou, rhettinger, serhiy.storchaka, stutzbach
优先级: low 关键字: patch

giampaolo.rodola2011-11-22 01:03 创建。最近一次由 admin2022-04-11 14:57 修改。

文件
文件名 上传时间 Description 编辑
sched-cancel-speedup.patch giampaolo.rodola, 2011-11-22 01:03 review
cancel-later-approach.patch giampaolo.rodola, 2011-11-22 09:03 review
cancel.patch giampaolo.rodola, 2011-11-26 13:35 review
bench.py giampaolo.rodola, 2011-11-26 13:36
cancel_2.patch serhiy.storchaka, 2012-10-08 12:08 review
cancel3.patch giampaolo.rodola, 2013-10-15 20:13 review
cancel_4.patch serhiy.storchaka, 2013-10-15 21:37 review
cancel_4b.patch serhiy.storchaka, 2013-10-21 15:33 review
Pull Requests
URL Status Linked Edit
PR 22759 open serhiy.storchaka, 2020-10-19 08:08
Messages (20)
msg148103 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) 日期: 2011-11-22 01:03
<snippet>
# bench.py
import sched, time
events = []
scheduler = sched.scheduler(time.time, time.sleep)
for x in range(4000):
    scheduler.enter(1, 1, lambda: None, ())
t = time.time()
for x in scheduler._queue:
    scheduler.cancel(x)
print(time.time() - t)
</snippet>

Before the patch:
9.433167934417725

After the patch:
1.3120810985565186


I have another approach in mind, which avoids removing the element from the queue immediately, and which should be an order of magnitude faster, but I'll provide that as a separate patch since it poses questions about API and backward compatibility.
msg148105 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) 日期: 2011-11-22 02:13
Can you post your other patch too?  I would like to review both at the same time.
msg148108 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) 日期: 2011-11-22 09:03
In attachment. 

Before the patch:
9.433167934417725

After the patch:
0.0016150474548339844

scheduler.queue and scheduler.empty should be modified in accordance (which I haven't done, it's just to give you an idea).
msg148403 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) 日期: 2011-11-26 13:35
New patch in attachment takes care of modifying empty() and queue property according with the new implementation.
With this, the API behaves the same as before (this was my main concern).
Also, it's smarter when it comes to cleaning up too many pending cancelled items:

if self._cancellations > 50 \
  and self._cancellations > (len(self._queue) >> 1):
     ...

Also, I made a little benchmark script (in attachment) to make sure that the speed of the rest of the API hasn't been significantly affected by this change:


BEFORE THE PATCH

test_cancel                    : time=0.66648 : calls=1 : stdev=0.00000
test_empty                     : time=0.00026 : calls=1 : stdev=0.00000
test_enter                     : time=0.00309 : calls=1 : stdev=0.00000
test_queue                     : time=6.20777 : calls=1 : stdev=0.00000
test_run                       : time=0.00746 : calls=1 : stdev=0.00000


AFTER THE PATCH

test_cancel                    : time=0.00054 : calls=1 : stdev=0.00000
test_empty                     : time=0.00031 : calls=1 : stdev=0.00000
test_enter                     : time=0.00375 : calls=1 : stdev=0.00000
test_queue                     : time=6.30314 : calls=1 : stdev=0.00000
test_run                       : time=0.00716 : calls=1 : stdev=0.00000
msg149292 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) 日期: 2011-12-12 11:50
Thread locks introduced in issue8684 should make this change more robust.
If this patch is reasonable, I'd like to commit it before the one in issue8684 for simplicity.
Raymond?
msg172377 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2012-10-08 12:08
In principle this is the right approach. But the time of enter() is increased by 20%. Here is updated and slightly optimized patch that restores enter() performance (but a little slow down cancel()). Because enter() is executed for each event and cancel() is not, and enter's gain greater cancel's loss, I think it is a very profitable exchange.
msg199978 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2013-10-15 06:26
Giampaolo, Raymond? What are your opinions?
msg199988 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) 日期: 2013-10-15 10:23
Serhiy, perhaps it would be useful to see if such optimizations can apply to Tulip's (or asyncio's) event loop, since it will probably be the new standard in 3.4.
msg200012 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2013-10-15 18:26
Yes, I will see.
msg200015 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) 日期: 2013-10-15 18:52
I don't have time to look into Serhiy's changes right now but here's a brief summary:

- there's a (I think) *minor* downside in terms of backward compatibility because scheduler._queue won't be updated after cancel() (basically this is the reason why this issue was waiting for Raymond's approval)

- the upside (other than the great speedup) is that I doubt anyone relies on that and scheduler._queue is not supposed to be used in the first place

- tulip's scheduler already provides something very similar to what this patch proposes so no action should be taken on that front


I personally think this should go in but I'd like to hear an OK from Raymond first.
msg200016 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) 日期: 2013-10-15 19:08
Sorry, I failed to notice there's a scheduler.queue property which exposes the underlying _queue attribute so the patch should take that into account and return the updated list.
msg200017 - (view) Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) 日期: 2013-10-15 20:13
Patch in attachment applies cleanly with the current 3.4 code (last one wasn't) and returns an updated list on scheduler.queue.

I rebased my work starting from my original patch (cancel.patch) not 	Serhiy's because it wasn't clear to me *where* exactly the enter() speedup was introduced (enter() method apparently is untouched by cancel_2.patch).
msg200023 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2013-10-15 21:37
> it wasn't clear to me *where* exactly the enter() speedup was introduced

Constructing Event object. You introduced __init__().

Here is a patch which is based on my patch and new Giampaolo's patch. In additional it fixes a performance for the queue property (perhaps regression was introduced in issue18432).

Unpatched:

test_cancel                    : time=4.05863 : calls=1 : stdev=0.00000
test_empty                     : time=0.00499 : calls=1 : stdev=0.00000
test_enter                     : time=0.03537 : calls=1 : stdev=0.00000
test_queue                     : time=37.82003 : calls=1 : stdev=0.00000
test_run                       : time=0.05289 : calls=1 : stdev=0.00000

cancel3.patch:

test_cancel                    : time=0.00649 : calls=1 : stdev=0.00000
test_empty                     : time=0.00704 : calls=1 : stdev=0.00000
test_enter                     : time=0.03959 : calls=1 : stdev=0.00000
test_queue                     : time=45.34278 : calls=1 : stdev=0.00000
test_run                       : time=0.05477 : calls=1 : stdev=0.00000

cancel_4.patch:

test_cancel                    : time=0.00889 : calls=1 : stdev=0.00000
test_empty                     : time=0.00636 : calls=1 : stdev=0.00000
test_enter                     : time=0.03092 : calls=1 : stdev=0.00000
test_queue                     : time=3.93284 : calls=1 : stdev=0.00000
test_run                       : time=0.05294 : calls=1 : stdev=0.00000
msg200043 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2013-10-16 09:10
All patches have problem with stable order. Rehashifying change it. But there are even more serious problems with current code (see issue19270).
msg200792 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2013-10-21 14:51
In updated patch I have reverted queue optimization (this should be separated issue) and made some minor changes.
msg200802 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2013-10-21 15:33
Fixed a bug in previous patch.
msg219316 - (view) Author: STINNER Victor (vstinner) * (Python committer) 日期: 2014-05-28 22:28
> Serhiy, perhaps it would be useful to see if such optimizations can apply to Tulip's (or asyncio's) event loop, since it will probably be the new standard in 3.4.

asyncio was designed differently. Cancelling a task doesn't remove it from a list of pending tasks. Cancelled tasks are just skipped when the event loop executes tasks.

If you look more closely, a "task" can be a Handle, Future or Task object. A Handle object has a _cancelled attribute, its cancel() method just sets this attribute to True. It's almost the same for a Future object. In the context of a Task object, cancel() is very different because it sends a CancelledError exception into the running code.

I see no possible optimization here.
msg228045 - (view) Author: STINNER Victor (vstinner) * (Python committer) 日期: 2014-10-01 00:46
> I see no possible optimization here.

The asyncio was just optimized to handle cancellation of many callbacks, see issue #22448.
msg267780 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) 日期: 2016-06-08 05:08
Ping.
msg350176 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) 日期: 2019-08-22 09:39
I no longer think this should be done. For most applications, cancel() speed is the least important task and isn't worth adding any extra baggage to the run() loop.  The current cancel() code is only slow if the length is somewhat large (atypical for a scheduling app).  Also, to my eyes the patch more than doubles the complexity of the module (which can currently be almost completely understood by examining the short run-loop).  Lastly, a lazy cancel() keeps the references around longer (which may be undesirable for some apps).

If you really think this module needs a lazy cancel(), then press ahead.  Otherwise, we have no evidence that this a problem in the real world.  The current cancel call is O(n) but runs at C speed which should be plenty fast enough for most cases.
历史
日期 用户 动作 参数
2022-04-11 14:57:24admin修改github: 57660
2020-10-27 04:05:56vstinner修改抄送: - vstinner
2020-10-19 08:08:18serhiy.storchaka修改pull_requests: + pull_request21724
2019-08-22 09:39:06rhettinger修改assignee: rhettinger -> serhiy.storchaka
消息: + msg350176
versions: + Python 3.9, - Python 3.4
2019-05-02 04:18:21josiahcarlson修改抄送: - josiahcarlson, josiah.carlson
2016-06-08 05:08:52serhiy.storchaka修改消息: + msg267780
2014-10-01 00:46:50vstinner修改消息: + msg228045
2014-05-28 22:28:54vstinner修改抄送: + vstinner
消息: + msg219316
2013-10-21 15:33:35serhiy.storchaka修改文件: + cancel_4b.patch

消息: + msg200802
2013-10-21 15:32:24serhiy.storchaka修改文件: - cancel_4a.patch
2013-10-21 15:05:32serhiy.storchaka修改dependencies: - sched.cancel() doesn't distinguish equal events and can break order
2013-10-21 14:51:30serhiy.storchaka修改文件: + cancel_4a.patch

消息: + msg200792
2013-10-16 09:10:25serhiy.storchaka修改消息: + msg200043
2013-10-16 08:49:35serhiy.storchaka修改dependencies: + sched.cancel() doesn't distinguish equal events and can break order
2013-10-15 21:37:46serhiy.storchaka修改文件: + cancel_4.patch

消息: + msg200023
2013-10-15 20:13:24giampaolo.rodola修改文件: + cancel3.patch

消息: + msg200017
2013-10-15 19:08:36giampaolo.rodola修改消息: + msg200016
2013-10-15 18:52:38giampaolo.rodola修改消息: + msg200015
2013-10-15 18:26:39serhiy.storchaka修改消息: + msg200012
2013-10-15 10:23:01pitrou修改抄送: + pitrou
消息: + msg199988
2013-10-15 06:26:56serhiy.storchaka修改消息: + msg199978
2012-10-24 09:34:07serhiy.storchaka修改stage: patch review
2012-10-08 12:08:29serhiy.storchaka修改文件: + cancel_2.patch
抄送: + serhiy.storchaka
消息: + msg172377

2012-10-08 06:14:58giampaolo.rodola修改versions: + Python 3.4, - Python 3.3
2011-12-12 11:50:25giampaolo.rodola修改消息: + msg149292
2011-11-26 13:36:03giampaolo.rodola修改文件: + bench.py
2011-11-26 13:35:32giampaolo.rodola修改文件: + cancel.patch
抄送: + josiahcarlson, josiah.carlson
消息: + msg148403

2011-11-22 09:03:42giampaolo.rodola修改文件: + cancel-later-approach.patch

消息: + msg148108
2011-11-22 02:13:48rhettinger修改优先级: normal -> low
消息: + msg148105

assignee: rhettinger
components: + Library (Lib)
type: performance
2011-11-22 01:03:42giampaolo.rodola创建