Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 01 Jan 2008 15:58:25 -0800
From:      Julian Elischer <julian@elischer.org>
To:        Bruce Evans <brde@optusnet.com.au>
Cc:        FreeBSD Net <freebsd-net@freebsd.org>
Subject:   Re: m_freem()
Message-ID:  <477AD3A1.4060401@elischer.org>
In-Reply-To: <20080101105918.I9594@delplex.bde.org>
References:  <4779697A.4050806@elischer.org> <20080101105918.I9594@delplex.bde.org>

next in thread | previous in thread | raw e-mail | index | archive | help
Bruce Evans wrote:
> On Mon, 31 Dec 2007, Julian Elischer wrote:
> 
>> m_freem() would be a perfect candidate for an inline function.
>> (or even macro).
>> in the case where m is null, no function call would even be made...
>> The whole function is only 2 lines, and it gets called once for every 
>> packet :-)
> 
> On the contrary, m_freem() is a large function that is fairly unsuitable
> for inlining.  I just happened to count that it usually takes 180
> instructions in -current with no INVARIANTS etc. (down from 245
> instructions in ~5.2).  Further counting gave 112 and 132 instructions
> for it (180 was for ttcp udp input packets and 112 and 132 are for
> ping packets in 2 directions).
> 
> m_freem() is only one statement, but that statement consists mainly
> of a function call to a function that is inline (m_free()).  m_free()
> sometimes calls m_free_ext(), which is not inline, and usually calls
> uma_zfree(), which is inline, but which is just a wrapper for
> uma_zfree_arg(), which is not inline.  uma_zfree_arg() is very large
> and thus very unsuitable for inlining.  I didn't check for [nested]
> inlining of its internals at the source level.  At runtime it usually
> calls the non-inline-function m_dtor_mbuf() which calls the non-inline
> function m_tag_delete_chain(); then it calls critical_enter() and
> critical_exit().  critical_exit() is fairly large and sometimes calls
> thread_lock(), mi_switch() and thread_unlock(), but usually doesn't.
> So the non-inline part of the call chain is usually:
> 
>     m_freem()
>       uma_zfree_arg()     # the following is just 1 short path through this
>     m_dtor_mbuf()
>       m_tag_delete_chain()
>     critical_enter()
>     critical_exit()
> 
> [Pause to recover from a double fault panic in critical*().  critical*()
> or kdb is non-reeantrant somehwere, so tracing through critical_*() or
> one of its callers in order to count instructions tends to cause panics.]
> 
> All this is too large to inline.  Inlining only the top level of it would
> only make a tiny difference.  It might make a positive or negative
> difference, depending on whether the reduced instruction count has a larger
> effect than the increased cache pressure.  Generally I think it is bogus
> to inline at the top level.  Here inlining at the top level may win in 2
> ways:
> - by avoiding the function call to the next level (and thus all function
>   calls) in the usual case.  I think this doesn't happen here.  I think it
>   is the usual case for the m_free_ext() call in m_free(), so inlining
>   m_free() is a clear win.
> - by improving branch prediction.  With a branch in a non-inline function,
>   it may be mispredicted often because different classes of callers
>   make it go in different ways.  With branch the distributed in callers
>   by inlining, it can be predicted perfectly in individual callers
>   that don't change its direction often and/or change its direction
>   in predictable ways.  On Athlon CPUs, mispredicting a single branch
>   costs the same several function calls provided the implicit branches
>   for all the function calls are not mispredicted.  Too much inlining
>   is still bad.  Apart from busting icaches, it can bust branch
>   prediction caches -- with enough distribution of branches, all
>   branches will be mispredicted.
> 
> The m_freem() wrapper currently limits the icache bloat from the
> m_free() inline.  In RELENG_4, both m_free() and m_freem() are non-inline
> and non-macro.  That may be why networking in RELENG_4 is so much more
> efficient than in -current ;-).  (Actually it makes little difference.)


Interesting.. I hadn't realised that m_free() had become an inline.
It does make things more interesting.



> 
> Bruce
> _______________________________________________
> freebsd-net@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-net
> To unsubscribe, send any mail to "freebsd-net-unsubscribe@freebsd.org"




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?477AD3A1.4060401>