Hi,

On 2022-09-20 12:45:24 +0300, Aleksander Alekseev wrote:
> > I'm not sure how to avoid any undefined behaviour without locks though.
> > Even with locks, performance is much better. But is it good enough for 
> > production?
>
> Potentially you could avoid taking locks by utilizing atomic
> operations and lock-free algorithms. But these algorithms are
> typically error-prone and not always produce a faster code than the
> lock-based ones. I'm pretty confident this is out of scope of this
> particular patch.

Why would you need lockfree operations?  All you need to do is to read
BufferDesc->state into a local variable and then make decisions based on that?


> +     for (int i = 0; i < NBuffers; i++)
> +     {
> +             BufferDesc *bufHdr;
> +             uint32          buf_state;
> +
> +             bufHdr = GetBufferDescriptor(i);
> +
> +             /* Lock each buffer header before inspecting. */
> +             buf_state = LockBufHdr(bufHdr);
> +
> +             /* Invalid RelFileNumber means the buffer is unused */
> +             if (RelFileNumberIsValid(BufTagGetRelNumber(&bufHdr->tag)))
> +             {
> +                     buffers_used++;
> +                     usagecount_avg += BUF_STATE_GET_USAGECOUNT(buf_state);
> +
> +                     if (buf_state & BM_DIRTY)
> +                             buffers_dirty++;
> +             }
> +             else
> +                     buffers_unused++;
> +
> +             if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
> +                     buffers_pinned++;
> +
> +             UnlockBufHdr(bufHdr, buf_state);
> +     }

I.e. instead of locking the buffer header as done above, this could just do
something along these lines:

        BufferDesc *bufHdr;
        uint32      buf_state;

        bufHdr = GetBufferDescriptor(i);

                buf_state = pg_atomic_read_u32(&bufHdr->state);

                if (buf_state & BM_VALID)
        {
            buffers_used++;
            usagecount_avg += BUF_STATE_GET_USAGECOUNT(buf_state);

            if (buf_state & BM_DIRTY)
                buffers_dirty++;
        }
        else
            buffers_unused++;

        if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
            buffers_pinned++;


Without a memory barrier you can get very slightly "out-of-date" values of the
state, but that's fine in this case.

Greetings,

Andres Freund


Reply via email to