On Fri, Dec 23, 2022 at 4:33 PM John Naylor
<john.nay...@enterprisedb.com> wrote:
>
>
>
> On Thu, Dec 22, 2022 at 10:00 PM Masahiko Sawada <sawada.m...@gmail.com> 
> wrote:
>
> > If the value is a power of 2, it seems to work perfectly fine. But for
> > example if it's 700MB, the total memory exceeds the limit:
> >
> > 2*(1+2+4+8+16+32+64+128) = 510MB (72.8% of 700MB) -> keep going
> > 510 + 256 = 766MB -> stop but it exceeds the limit.
> >
> > In a more bigger case, if it's 11000MB,
> >
> > 2*(1+2+...+2048) = 8190MB (74.4%)
> > 8190 + 4096 = 12286MB
> >
> > That being said, I don't think they are not common cases. So the 75%
> > threshold seems to work fine in most cases.
>
> Thinking some more, I agree this doesn't have large practical risk, but 
> thinking from the point of view of the community, being loose with memory 
> limits by up to 10% is not a good precedent.

Agreed.

> Perhaps we can be clever and use 75% when the limit is a power of two and 50% 
> otherwise. I'm skeptical of trying to be clever, and I just thought of an 
> additional concern: We're assuming behavior of the growth in size of new DSA 
> segments, which could possibly change. Given how allocators are typically 
> coded, though, it seems safe to assume that they'll at most double in size.

Sounds good to me.

I've written a simple script to simulate the DSA memory usage and the
limit. The 75% limit works fine for a power of two cases, and we can
use the 60% limit for other cases (it seems we can use up to about 66%
but used 60% for safety). It would be best if we can mathematically
prove it but I could prove only the power of two cases. But the script
practically shows the 60% threshold would work for these cases.

Regards

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com
# Prepare an array for the total amount of memory allocated in DSA.
dsa_total_memory = [0] * 40
dsa_total_memory[0] = 1
dsa_total_memory[1] = dsa_total_memory[0] + 1
for i in range(1, 20):
    dsa_memory = 1 << (i)
    dsa_total_memory[i * 2] = dsa_total_memory[(i * 2) - 1] + dsa_memory
    dsa_total_memory[(i * 2) + 1] = dsa_total_memory[i * 2] + dsa_memory
#print(dsa_total_memory)

# The array 'a' has values for maintenance_work_mem, i.e., the threthold. For each value
# in the given array 'a', this function checks the 'ratio'% threthold would work.
def check(a, ratio, verbose):
    ret = True
    for i in a:
        have_ng = False
        mwm = i
        mwm_limit = mwm * ratio
        for dsa in dsa_total_memory:
            if mwm_limit <= dsa:
                # The DSA memory usage exceeded the N% threshold. Check also
                # if it exceeded the maintenance_work_mem too.
                if mwm < dsa:
                    if verbose:
                        print("total=%d %d%%=%d ... FAIL (usage=%d)" % (mwm, ratio * 100, mwm_limit, dsa))
                    ret = False
                    have_ng = True
                break
        if not have_ng and verbose:
            print("all OK (total=%d %d%%=%d)" % (mwm, ratio * 100, mwm_limit))

    return ret

def check_ratio(a, ratio_begin=60, ratio_end=76, ratio_incr=1):
    for ratio in range(ratio_begin, ratio_end, ratio_incr):
        ret = check(a, ratio / 100, False)
        if ret:
            print("ratio %d%% ok" % ratio)
        else:
            print("ratio %d%% FAILED" % ratio)

# power-of-2, such as 1024kB, 2048kB, 4096kB, 8192kB, ...
po2 = []
for i in range(1, 20):
    po2.append(1 << (i + 10))

# from 1024kB(1MB) to 2000MB by 1024
normal_1024 = []
for i in range(1, 2000):
    normal_1024.append(1024 * i)

# from 1024 by 1000
normal_1000 = []
for i in range(1, 2000):
    normal_1000.append(1024 + (1000 * i))

print("test power-of-2")
check_ratio(po2)
print("test normal 1024")
check_ratio(normal_1024)
print("test normal 1000")
check_ratio(normal_1000)

Reply via email to