All of lore.kernel.org
 help / color / mirror / Atom feed
* [REV2: PATCH 0/2] workqueue: Two API's to update delayed works quickly
@ 2008-09-29  7:03 Krishna Kumar
  2008-09-29  7:03 ` [REV2: PATCH 1/2]: workqueue: Implement the kernel API Krishna Kumar
  2008-09-29  7:03 ` [REV2: PATCH 2/2]: workqueue: Modify some users to use the new API Krishna Kumar
  0 siblings, 2 replies; 8+ messages in thread
From: Krishna Kumar @ 2008-09-29  7:03 UTC (permalink / raw)
  To: linux-kernel; +Cc: Oleg Nesterov, Krishna Kumar

From: Krishna Kumar <krkumar2@in.ibm.com>

Implement two API's for quickly updating delayed works:
	int schedule_update_delayed_work(struct delayed_work *dwork,
					  unsigned long delay);
	int queue_update_delayed_work(struct workqueue_struct *wq,
				       struct delayed_work *dwork,
				       unsigned long delay);

I tested the following combinations, after Oleg suggested some improvements:
	A. - Original submission.
		1 CPU -> Org: 474213	New: 55365	Time saved: 88.3%
		4 CPU -> Org: 3650631	New: 225160	Time saved: 93.8%
	B1. - Oleg's suggestion to avoid costly __cancel_work_timer.
		1 CPU -> Org: 474213	New: 233714	Time saved: 50.7%
		4 CPU -> Org: 3650631	New: 959455	Time saved: 73.7%
	B2. - B1 + check for same expiry.
		1 CPU -> Org: 474213	New: 72276	Time saved: 84.8%
		4 CPU -> Org: 3650631	New: 301510	Time saved: 91.7%
	C. - Merge Oleg's idea with code A.
		1 CPU -> Org: 474213	New: 55160	Time saved: 88.4%
		4 CPU -> Org: 3650631	New: 215369	Time saved: 94.1%

Merged code seems to perform the best, though the difference with the original
submission is only marginal. Code snippets below with comments removed:

A: Original submission:
-----------------------
void queue_update_delayed_work(struct workqueue_struct *wq,
			       struct delayed_work *dwork, unsigned long delay)
{
	struct work_struct *work = &dwork->work;

	if (likely(test_and_set_bit(WORK_STRUCT_PENDING,
				    work_data_bits(work)))) {
		struct timer_list *timer = &dwork->timer;

		if (jiffies + delay == timer->expires)
			return;

		__cancel_work_timer_internal(work, timer);
	}

	__queue_delayed_work(-1, dwork, work, wq, delay);
}

B1: Oleg suggestion:
------------------------
(I optimized a bit as parallel updates are not important, only one needs to
succeed)

int queue_update_delayed_work(struct workqueue_struct *wq,
			      struct delayed_work *dwork, unsigned long delay)
{
	unsigned long when = jiffies + delay;

	if (queue_delayed_work(wq, dwork, delay))
		return 1;

	if (update_timer_expiry(&dwork->timer, when))
		return 0;

	cancel_work_sync(&dwork->work);
	queue_delayed_work(wq, dwork, delay);
	return 0;
}

B2: B1 + extra check:
---------------------
int queue_update_delayed_work(struct workqueue_struct *wq,
			      struct delayed_work *dwork, unsigned long delay)
{
	unsigned long when = jiffies + delay;

	if (queue_delayed_work(wq, dwork, delay))
		return 1;

	if (when == dwork->timer.expires ||
	    update_timer_expiry(&dwork->timer, when))
		return 0;

	cancel_work_sync(&dwork->work);
	queue_delayed_work(wq, dwork, delay);
	return 0;
}

C: Combined code:
-----------------
int queue_update_delayed_work(struct workqueue_struct *wq,
			      struct delayed_work *dwork, unsigned long delay)
{
	struct work_struct *work = &dwork->work;

	if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) {
		__queue_delayed_work(-1, dwork, work, wq, delay);
		return 1;
	} else {
		struct timer_list *timer = &dwork->timer;
		unsigned long when = jiffies + delay;
		int ret;

		if (timer->expires == when)
			return 0;

		do {
			if (likely(update_timer_expiry(timer, when)))
				return 0;

			ret = try_to_grab_pending(work);
			if (ret < 0)
				wait_on_work(work);
		} while (ret < 0);

		__queue_delayed_work(-1, dwork, work, wq, delay);
		return 0;
	}
}

I am submitting the merged code. Please review and provide feedback.

Thanks

- KK

[PATCH 1/2]: Implement the kernel API's
[PATCH 2/2]: Modify some drivers to use the new APIs instead of the old ones

Signed-off-by: Krishna Kumar <krkumar2@in.ibm.com>
---

(Description of the patches from first submission:
These API's are useful to update an existing work entry more efficiently (but
can be used to queue a new work entry too) when the operation is done very
frequently. The rationale is to save time of first cancelling work/timer and
adding work/timer when the same work is added many times in quick succession.

queue_update_delayed_work_on() and schedule_update_delayed_work_on() are not
implemented as there are no users which can take advantage of these variations.

Example possible users (FS's, wireless drivers):
------------------------------------------------
	lbs_scan_networks, nfs4_renew_state, ocfs2_schedule_truncate_log_flush,
	nfs4_schedule_state_renewal, afs_flush_callback_breaks, afs_reap_server,
	afs_purge_servers, afs_vlocation_reaper, afs_vlocation_purge,
	o2hb_arm_write_timeout, lbs_scan_networks, isr_indicate_rf_kill,
	lbs_postpone_association_work, isr_scan_complete, ipw_radio_kill_sw,
	 __ipw_led_activity_on, ipw_radio_kill_sw, ds2760_battery_resume, etc.

Performance (numbers in jiffies):
---------------------------------
These API's are useful in following cases:
	a. add followed by cancel+add with long period between the add's. Time
	   saved is the time to clear and re-setting the WORK_STRUCT_PENDING
	   bit, plus the reduction of one API call.
	b. add followed by cancel+add with very short time between the add's.
	   Time saved is all of above, plus avoid cancelling and re-adding.
)

^ permalink raw reply	[flat|nested] 8+ messages in thread
* Re: [REV2: PATCH 1/2]: workqueue: Implement the kernel API
@ 2008-09-30 16:25 Krishna Kumar
  0 siblings, 0 replies; 8+ messages in thread
From: Krishna Kumar @ 2008-09-30 16:25 UTC (permalink / raw)
  To: linux-kernel; +Cc: oleg

Hi Oleg,

(sending from a different email address)

> Oleg Nesterov <oleg@tv-sign.ru> wrote on 09/29/2008 07:57:34 PM:
>
> Yes. I must admit, I prefer the simple, non-intrusive code I suggested
> much more.
>
> Once again, the slow path is (at least, supposed to be) unlikely, and
> the difference is not that large. (I mean the slow path is when both
> queue() and update_timer() fail).
>
> Should we complicate the code to add this minor optimization (and
> btw pessimize the "normal" queue_delayed_work) ?
>
> And, once we have the correct and simple code, we can optimize it
> later.
>
> > I will go with the above
> > approach.
>
> No. Please do what _you_ think right ;)

No, you are right - I will go to the simpler (and bug-free?) interface.

> Yes. Please note that queue_delayed_work(work, 0) does not use the timer
> at all. IOW, queue_delayed_work(wq, work, 0) == queue_work(wq, &dwork->work).
> Perhaps (I don't know) update_queue_delayed_work() should do the same.
>
> From the next patch:
>
>       -       cancel_delayed_work(&afs_vlocation_reap);
>       -       schedule_delayed_work(&afs_vlocation_reap, 0);
>       +       schedule_update_delayed_work(&afs_vlocation_reap, 0);
>
> Again, I don't claim this is wrong, but please note that delay == 0.

As you stated in an earlier mail, the following code should handle all cases.
I think delay==0 is fine now, we take the costly (but rare) path.

int queue_update_delayed_work(struct workqueue_struct *wq,
                              struct delayed_work *dwork, unsigned long delay)
{
        int ret = 1;

        while (queue_delayed_work(wq, dwork, delay)) {
                unsigned long when = jiffies + delay;

                ret = 0;
                if (delay && update_timer_expiry(&dwork->timer, when))
                        break;
                cancel_work_sync(&dwork->work);
        }

        return ret;
}

I will run some tests and submit again.

Thanks once more for explaining patiently some very complicated portions :)

- KK


      Unlimited freedom, unlimited storage. Get it now, on http://help.yahoo.com/l/in/yahoo/mail/yahoomail/tools/tools-08.html/

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-09-30 16:31 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-29  7:03 [REV2: PATCH 0/2] workqueue: Two API's to update delayed works quickly Krishna Kumar
2008-09-29  7:03 ` [REV2: PATCH 1/2]: workqueue: Implement the kernel API Krishna Kumar
2008-09-29 14:27   ` Oleg Nesterov
2008-09-30 11:08     ` Krishna Kumar2
2008-09-30 13:15       ` Oleg Nesterov
2008-09-29  7:03 ` [REV2: PATCH 2/2]: workqueue: Modify some users to use the new API Krishna Kumar
2008-09-29 14:45   ` Oleg Nesterov
  -- strict thread matches above, loose matches on Subject: below --
2008-09-30 16:25 [REV2: PATCH 1/2]: workqueue: Implement the kernel API Krishna Kumar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.