Re: [PATCH v4 2/2] rcuperf: Add kfree_rcu() performance Tests

From: Joel Fernandes
Date: Tue Aug 20 2019 - 20:27:26 EST


On Mon, Aug 19, 2019 at 07:50:56PM -0700, Paul E. McKenney wrote:

> > > > > > + do {
> > > > > > + for (i = 0; i < kfree_alloc_num; i++) {
> > > > > > + alloc_ptrs[i] = kmalloc(sizeof(struct kfree_obj), GFP_KERNEL);
> > > > > > + if (!alloc_ptrs[i])
> > > > > > + return -ENOMEM;
> > > > > > + }
> > > > > > +
> > > > > > + for (i = 0; i < kfree_alloc_num; i++) {
> > > > > > + if (!kfree_no_batch) {
> > > > > > + kfree_rcu(alloc_ptrs[i], rh);
> > > > > > + } else {
> > > > > > + rcu_callback_t cb;
> > > > > > +
> > > > > > + cb = (rcu_callback_t)(unsigned long)offsetof(struct kfree_obj, rh);
> > > > > > + kfree_call_rcu_nobatch(&(alloc_ptrs[i]->rh), cb);
> > > > > > + }
> > > > > > + }
> > > > >
> > > > > The point of allocating a large batch and then kfree_rcu()ing them in a
> > > > > loop is to defeat the per-CPU pool optimization? Either way, a comment
> > > > > would be very good!
> > > >
> > > > It was a reasoning like this, added it as a comment:
> > > >
> > > > /* While measuring kfree_rcu() time, we also end up measuring kmalloc()
> > > > * time. So the strategy here is to do a few (kfree_alloc_num) number
> > > > * of kmalloc() and kfree_rcu() every loop so that the current loop's
> > > > * deferred kfree()ing overlaps with the next loop's kmalloc().
> > > > */
> > >
> > > The thought being that the CPU will be executing the two loops
> > > concurrently? Up to a point, agreed, but how much of an effect is
> > > that, really?
> >
> > Yes it may not matter much. It was just a small thought when I added the
> > loop, I had to start somewhere, so I did it this way.
> >
> > > Or is the idea to time the kfree_rcu() loop separately? (I don't see
> > > any such separate timing, though.)
> >
> > The kmalloc() times are included within the kfree loop. The timing of
> > kfree_rcu() is not separate in my patch.
>
> You lost me on this one. What happens when you just interleave the
> kmalloc() and kfree_rcu(), without looping, compared to the looping
> above? Does this get more expensive? Cheaper? More vulnerable to OOM?
> Something else?

You mean pairing a single kmalloc() with a single kfree_rcu() and doing this
several times? The results are very similar to doing kfree_alloc_num
kmalloc()s, then do kfree_alloc_num kfree_rcu()s; and repeat the whole thing
kfree_loops times (as done by this rcuperf patch we are reviewing).

Following are some numbers. One change is the case where we are not at all
batching does seem to complete even faster when we fully interleave kmalloc()
with kfree() while the case of batching in the same scenario completes at the
same time as did the "not fully interleaved" scenario. However, the grace
period reduction improvements and the chances of OOM'ing are pretty much the
same in either case.

Fully interleaved, single kmalloc followed by kfree_rcu, do this kfree_alloc_num * kfree_loops times.
=======================
(1) Batching
rcuperf.kfree_loops=20000 rcuperf.kfree_alloc_num=8000 rcuperf.kfree_no_batch=0 rcuperf.kfree_rcu_test=1

root@(none):/# free -m
total used free shared buff/cache available
Mem: 977 261 675 0 39 674

[ 15.635620] Total time taken by all kfree'ers: 14255673998 ns, loops: 20000, batches: 1596

(2) No Batching
rcuperf.kfree_loops=20000 rcuperf.kfree_alloc_num=8000 rcuperf.kfree_no_batch=1 rcuperf.kfree_rcu_test=1

root@(none):/# free -m
total used free shared buff/cache available
Mem: 977 67 870 0 39 869
Swap: 0 0 0

[ 12.365872] Total time taken by all kfree'ers: 10902137101 ns, loops: 20000, batches: 6893


Not fully interleaved: do kfree_alloc_num kmallocs, then do kfree_alloc_num kfree_rcu()s. And repeat this kfree_loops times.
=======================
(1) Batching
rcuperf.kfree_loops=20000 rcuperf.kfree_alloc_num=8000 rcuperf.kfree_no_batch=0 rcuperf.kfree_rcu_test=1

root@(none):/# free -m
total used free shared buff/cache available
Mem: 977 251 686 0 39 684
Swap: 0 0 0

[ 15.574402] Total time taken by all kfree'ers: 14185970787 ns, loops: 20000, batches: 1548

(2) No Batching
rcuperf.kfree_loops=20000 rcuperf.kfree_alloc_num=8000 rcuperf.kfree_no_batch=1 rcuperf.kfree_rcu_test=1

root@(none):/# free -m
total used free shared buff/cache available
Mem: 977 82 855 0 39 853
Swap: 0 0 0

[ 13.724554] Total time taken by all kfree'ers: 12246217291 ns, loops: 20000, batches: 7262


thanks,

- Joel