>-----Original Message-----
>From: Richard Purdie <richard.pur...@linuxfoundation.org>
>Sent: Thursday, June 30, 2022 5:53 AM
>To: Gupta, Aryaman <aryaman.gu...@windriver.com>; openembedded-
>c...@lists.openembedded.org
>Subject: Re: [OE-core] [PATCH] runqueue: add cpu/io pressure regulation
>
>[Please note: This e-mail is from an EXTERNAL e-mail address]
>
>On Wed, 2022-06-29 at 16:10 -0400, Aryaman Gupta wrote:
>> Stop the scheduler from starting new tasks if the current cpu or io
>> pressure is above a certain threshold, specified through the
>> "BB_MAX_{CPU|IO}_SOME_PRESSURE" variables in conf/local.conf.
>>
>> If the thresholds aren't specified, the default values are 100 for
>> both CPU and IO, which will have no impact on build times.
>> Arbitary lower limit of 1.0 results in a fatal error to avoid
>> extremely long builds. If the percentage limits are higher than 100,
>> then the default values are used and warnings are issued to inform
>> users that the specified limit is out of bounds.
>>
>> Signed-off-by: Aryaman Gupta <aryaman.gu...@windriver.com>
>> Signed-off-by: Randy Macleod <randy.macl...@windriver.com>
>> ---
>>  bitbake/lib/bb/runqueue.py | 39
>> ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 39 insertions(+)
>
>This looks like a good start, thanks. There are a few things which will need
>cleaning up in here as this is pretty performance sensitive code (try a "time
>bitbake world -n" to see what I mean).
>
>Firstly, it is a bitbake patch so should really go to the bitbake mailing 
>list, not OE-
>Core.
>

Right, will do!

>> diff --git a/bitbake/lib/bb/runqueue.py b/bitbake/lib/bb/runqueue.py
>> index 1e47fe70ef..9667acc11c 100644
>> --- a/bitbake/lib/bb/runqueue.py
>> +++ b/bitbake/lib/bb/runqueue.py
>> @@ -159,6 +159,27 @@ class RunQueueScheduler(object):
>>                  self.buildable.append(tid)
>>
>>          self.rev_prio_map = None
>> +        # Some hosts like openSUSE have readable /proc/pressure files
>> +        # but throw errors when these files are opened.
>> +        try:
>> +            subprocess.check_output(["cat", "/proc/pressure/cpu",
>"/proc/pressure/io"], \
>> +                                    universal_newlines=True, 
>> stderr=subprocess.DEVNULL)
>> +            self.readable_pressure_files = True
>>
>>         except:
>> +            if self.rq.max_cpu_pressure!=100 or 
>> self.rq.max_io_pressure!=100:
>> +                bb.warn("The /proc/pressure files can't be read. Continuing 
>> build
>without monitoring pressure")
>> +            self.readable_pressure_files = False
>> +
>> +    def exceeds_max_pressure(self):
>> +        if self.readable_pressure_files:
>> +            # extract avg10 from /proc/pressure/{cpu|io}
>> +            curr_pressure_sample = subprocess.check_output(["cat",
>"/proc/pressure/cpu", "/proc/pressure/io"], \
>> +                                   universal_newlines=True, 
>> stderr=subprocess.DEVNULL)
>> +            curr_cpu_pressure =
>curr_pressure_sample.split('\n')[0].split()[1].split("=")[1]
>> +            curr_io_pressure =
>> + curr_pressure_sample.split('\n')[2].split()[1].split("=")[1]
>
>This is horrible, you're adding in a fork() call for every pass through the
>scheduler code. You can just open() and read the file instead which will have
>*much* lower overhead.

Oh you're right. Randy and I tested this and turns out subprocess is 
significantly slower.
We also looked at another level of optimization where you open() once and 
re-use the file descriptor, 
similar to what's done in buildstats. While this is faster, the absolute time 
to run the code is around 1 or 3
microseconds. Given the added complexity to the code, the optimization does not 
seem worthwhile.
I'll remove the subprocess() call in v2.

>
>Even then, I'm not particularly thrilled to have this level of overhead in this
>codepath, in some ways I'd prefer to rate limit how often we're looking up this
>value rather than once per pass through the scheduler path. I'm curious what
>the timings say.
>
We wrote this simple test code:
import timeit, subprocess
file = "/proc/pressure/cpu"

def test_subprocess():
    subprocess.check_output(["cat", file])

test1 = open(file)
def test_seek():
    test1.seek(0)
    test1.read()

def test_openread():
    with open(file) as test:
        test.read()
        check_var = 1+1

repeat = 1000
#print("SUBPROCESS: %s " % timeit.Timer(test_subprocess).timeit(number=repeat))
print("SEEK: %s " % timeit.Timer(test_seek).timeit(number=repeat))
print("OPEN AND READ: %s " % timeit.Timer(test_openread).timeit(number=repeat))

Running this with python test.py gave:
SEEK: 0.012695984973106533 
OPEN AND READ: 0.0369647319894284

You can see that this is about 3x faster but only a couple of microseconds in 
absolute terms.
Do you think that's a reasonable overhead?

>> +
>> +            return float(curr_cpu_pressure) > self.rq.max_cpu_pressure or
>float(curr_io_pressure) > self.rq.max_io_pressure
>> +        return False
>>
>>      def next_buildable_task(self):
>>          """
>> @@ -171,6 +192,8 @@ class RunQueueScheduler(object):
>>          buildable.intersection_update(self.rq.tasks_covered |
>self.rq.tasks_notcovered)
>>          if not buildable:
>>              return None
>> +        if self.exceeds_max_pressure():
>> +            return None
>>
>>          # Filter out tasks that have a max number of threads that have been
>exceeded
>>          skip_buildable = {}
>> @@ -1699,6 +1722,8 @@ class RunQueueExecute:
>>
>>          self.number_tasks = int(self.cfgData.getVar("BB_NUMBER_THREADS") or
>1)
>>          self.scheduler = self.cfgData.getVar("BB_SCHEDULER") or "speed"
>> +        self.max_cpu_pressure =
>float(self.cfgData.getVar("BB_MAX_CPU_SOME_PRESSURE") or 100.0)
>> +        self.max_io_pressure =
>> + float(self.cfgData.getVar("BB_MAX_IO_SOME_PRESSURE") or 100.0)
>
>I did wonder if this should be BB_PRESSURE_MAX_SOME_IO as the order of the
>information kinds of seems backwards to me. That could just be me though! :)

Sure, that colour of bikeshed is perfect ;^)

Regards,
Aryaman
>
>Cheers,
>
>Richard
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#167444): 
https://lists.openembedded.org/g/openembedded-core/message/167444
Mute This Topic: https://lists.openembedded.org/mt/92073312/21656
Group Owner: openembedded-core+ow...@lists.openembedded.org
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to