public inbox for fortran@gcc.gnu.org
 help / color / mirror / Atom feed
From: "Zhu, Lipeng" <lipeng.zhu@intel.com>
To: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Cc: tkoenig@netcologne.de, fortran@gcc.gnu.org,
	gcc-patches@gcc.gnu.org, "Lu, Hongjiu" <hongjiu.lu@intel.com>,
	"Li, Tianyou" <tianyou.li@intel.com>,
	"Deng, Pan" <pan.deng@intel.com>,
	"Guo, Wangyang" <wangyang.guo@intel.com>
Subject: Re: [PATCH v3] libgfortran: Replace mutex with rwlock
Date: Mon, 8 May 2023 17:31:27 +0800	[thread overview]
Message-ID: <f29e541f-3029-6f2e-3a1f-798f9503a511@intel.com> (raw)



On 1/1/1970 8:00 AM, Bernhard Reutner-Fischer wrote:
> Hi!
> 
> [please do not top-post]
> 
Sure, thanks for the reminder.

> On Thu, 20 Apr 2023 21:13:08 +0800
> "Zhu, Lipeng" <lipeng.zhu@intel.com> wrote:
> 
>> Hi Bernhard,
>>
>> Thanks for your questions and suggestions.
>> The rwlock could allow multiple threads to have concurrent read-only
>> access to the cache/unit list, only a single writer is allowed.
> 
> right.
> 
>> Write lock will not be acquired until all read lock are released.
> 
> So i must have confused rwlock with something else, something that allows self to hold a read-lock and
> upgrade that to a write-lock, purposely starving all successive incoming readers. I.e. just toggle your
> RD_TO_WRLOCK impl, here, atomically. This proved to be benefical in some situations in the past.
> Doesn't seem to work with your rwlock, does it
> 
Pthread API doesn't support the upgrade rdlock to wrlock directly, the 
calling thread may deadlock if at the time the call is made it holds the 
read-write lock (whether a read or write lock). 
https://linux.die.net/man/3/pthread_rwlock_wrlock.
That is why we implement RD_TO_WRLOCK like this: release read lock 
firstly and then acquire write lock.

>> And I didn't change the mutex scope when refactor the code, only make
>> a more fine-grained distinction for the read/write cache/unit list.
> 
> Yes of course, i can see you did that.
> 
>> I complete the comment according to your template, I will insert the
>> comment in the source code in next version patch with other refinement
>> by your suggestions.
>> "
>> Now we did not get a unit in cache and unit list, so we need to create
>> a new unit, and update it to cache and unit list.
> 
> s/Now/By now/ or s/Now w/W/ and s/get/find/ "
> We did not find a unit in the cache nor in the unit list, create a new
> (locked) unit and insert into the unit list and cache.
> Manipulating either or both the unit list and the unit cache requires to hold a write-lock [for obvious
> reasons]"
> 
> Superfluous when talking about pthread_rwlock_wrlock since that implies that even the process
> acquiring the wrlock has to first release it's very own rdlock.
> 
Thanks for the correction, I will update the comments in next v4 patch.

>> Prior to update the cache and list, we need to release all read locks,
>> and then immediately to acquire write lock, thus ensure the exclusive
>> update to the cache and unit list.
>> Either way, we will manipulate the cache and/or the unit list so we
>> must take a write lock now.
>> We don't take the write bit in *addition* to the read lock because:
>> 1. It will needlessly complicate releasing the respective lock;
> 
> Under pthread_rwlock_wrlock it will deadlock, so that's wrong?
> Drop that point then? If not, what's your reasoning / observation?
> 
> Under my lock, you hold the R, additionally take the W and then immediately release the R because you
> yourself won't read, just write.
> But mine's not the pthread_rwlock you talk about, admittedly.
> 
Yes, pthread_rwlock doesn't support upgrade rdlock to wrlock directly, 
so we can’t hold rdlock while trying to acquire wrlock. I will drop the 
first point and update the comment accordingly.

>> 2. By separate the read/write lock, it will greatly reduce the
>> contention at the read part, while write part is not always necessary
>> or most unlikely once the unit hit in cache;
> 
> We know that.
> 
>> 3. We try to balance the implementation complexity and the performance
>> gains that fit into current cases we observed.
> 
> .. by just using a pthread_rwlock. And that's the top point iff you keep it at that. That's a fair step, sure.
> BTW, did you look at the RELEASE semantics, respectively the note that one day (and now is that very
> day), we might improve on the release semantics? Can of course be incremental AFAIC
> 
Not quite understand your question about looking at the RELEASE 
semantics. Can you be more specific?

>> "
> 
> If folks agree on this first step then you have my OK with a catchy malloc and the discussion recorded
> here on the list. A second step would be RELEASE.
> And, depending on the underlying capabilities of available locks, further tweaks, obviously.
> 
> PS: and, please, don't top-post
> 
> thanks,
> 
>>
>> Best Regards,
>> Zhu, Lipeng
>>
>> On 1/1/1970 8:00 AM, Bernhard Reutner-Fischer wrote:
>>> On 19 April 2023 09:06:28 CEST, Lipeng Zhu via Fortran <fortran@gcc.gnu.org> wrote:
>>>> This patch try to introduce the rwlock and split the read/write to
>>>> unit_root tree and unit_cache with rwlock instead of the mutex to
>>>> increase CPU efficiency. In the get_gfc_unit function, the
>>>> percentage to step into the insert_unit function is around 30%, in
>>>> most instances, we can get the unit in the phase of reading the
>>>> unit_cache or unit_root tree. So split the read/write phase by
>>>> rwlock would be an approach to make it more parallel.
>>>>
>>>> BTW, the IPC metrics can gain around 9x in our test server with 220
>>>> cores. The benchmark we used is https://github.com/rwesson/NEAT
>>>>   
>>>    
>>>> +#define RD_TO_WRLOCK(rwlock) \
>>>> +  RWUNLOCK (rwlock);\
>>>> +  WRLOCK (rwlock);
>>>> +#endif
>>>> +
>>>
>>>    
>>>> diff --git a/libgfortran/io/unit.c b/libgfortran/io/unit.c index
>>>> 82664dc5f98..4312c5f36de 100644
>>>> --- a/libgfortran/io/unit.c
>>>> +++ b/libgfortran/io/unit.c
>>>    
>>>> @@ -329,7 +335,7 @@ get_gfc_unit (int n, int do_create)
>>>>     int c, created = 0;
>>>>
>>>>     NOTE ("Unit n=%d, do_create = %d", n, do_create);
>>>> -  LOCK (&unit_lock);
>>>> +  RDLOCK (&unit_rwlock);
>>>>
>>>> retry:
>>>>     for (c = 0; c < CACHE_SIZE; c++) @@ -350,6 +356,7 @@ retry:
>>>>         if (c == 0)
>>>> 	break;
>>>>       }
>>>> +  RD_TO_WRLOCK (&unit_rwlock);
>>>
>>> So I'm trying to convince myself why it's safe to unlock and only then take the write lock.
>>>
>>> Can you please elaborate/confirm why that's ok?
>>>
>>> I wouldn't mind a comment like
>>> We can release the unit and cache read lock now. We might have to allocate a (locked) unit, below in
>>> do_create.
>>> Either way, we will manipulate the cache and/or the unit list so we have to take a write lock now.
>>>
>>> We don't take the write bit in *addition* to the read lock because..
>>>
>>> (that needlessly complicates releasing the respective locks / it triggers too much contention when
> we..
>>> / ...?)
>>>
>>> thanks,
>>>    
>>>>
>>>>     if (p == NULL && do_create)
>>>>       {
>>>> @@ -368,8 +375,8 @@ retry:
>>>>     if (created)
>>>>       {
>>>>         /* Newly created units have their lock held already
>>>> -	 from insert_unit.  Just unlock UNIT_LOCK and return.  */
>>>> -      UNLOCK (&unit_lock);
>>>> +	 from insert_unit.  Just unlock UNIT_RWLOCK and return.  */
>>>> +      RWUNLOCK (&unit_rwlock);
>>>>         return p;
>>>>       }
>>>>
>>>> @@ -380,7 +387,7 @@ found:
>>>>         if (! TRYLOCK (&p->lock))
>>>> 	{
>>>> 	  /* assert (p->closed == 0); */
>>>> -	  UNLOCK (&unit_lock);
>>>> +	  RWUNLOCK (&unit_rwlock);
>>>> 	  return p;
>>>> 	}
>>>>
>>>> @@ -388,14 +395,14 @@ found:
>>>>       }
>>>>
>>>>
>>>> -  UNLOCK (&unit_lock);
>>>> +  RWUNLOCK (&unit_rwlock);
>>>>
>>>>     if (p != NULL && (p->child_dtio == 0))
>>>>       {
>>>>         LOCK (&p->lock);
>>>>         if (p->closed)
>>>> 	{
>>>> -	  LOCK (&unit_lock);
>>>> +	  WRLOCK (&unit_rwlock);
>>>> 	  UNLOCK (&p->lock);
>>>> 	  if (predec_waiting_locked (p) == 0)
>>>> 	    destroy_unit_mutex (p);
>>>> @@ -593,8 +600,8 @@ init_units (void)
>>>> #endif
>>>> #endif
>>>>
>>>> -#ifndef __GTHREAD_MUTEX_INIT
>>>> -  __GTHREAD_MUTEX_INIT_FUNCTION (&unit_lock);
>>>> +#if (!defined(__GTHREAD_RWLOCK_INIT) &&
>>>> +!defined(__GTHREAD_MUTEX_INIT))
>>>> +  __GTHREAD_MUTEX_INIT_FUNCTION (&unit_rwlock);
>>>> #endif
>>>>
>>>>     if (sizeof (max_offset) == 8)
>>>    
> 

             reply	other threads:[~2023-05-08  9:31 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-08  9:31 Zhu, Lipeng [this message]
2023-05-08 10:04 ` Bernhard Reutner-Fischer
  -- strict thread matches above, loose matches on Subject: below --
2023-04-20 13:13 Zhu, Lipeng
2023-04-24 19:45 ` Bernhard Reutner-Fischer
     [not found] <20221230001607.2232962-1-lipeng.zhu () intel ! com>
2023-04-19  7:06 ` Lipeng Zhu
2023-04-19 12:51   ` Bernhard Reutner-Fischer
2023-04-19 14:50     ` Bernhard Reutner-Fischer
2023-04-19 21:49   ` Bernhard Reutner-Fischer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f29e541f-3029-6f2e-3a1f-798f9503a511@intel.com \
    --to=lipeng.zhu@intel.com \
    --cc=fortran@gcc.gnu.org \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=hongjiu.lu@intel.com \
    --cc=pan.deng@intel.com \
    --cc=rep.dot.nop@gmail.com \
    --cc=tianyou.li@intel.com \
    --cc=tkoenig@netcologne.de \
    --cc=wangyang.guo@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).