0

I'm a beginner and made this python script so that my computer will play "azan" during prayer times automatically. I realized that when my program is running, it uses 29.1% of my CPU. I suspect this is coming from:

while True:
    global azan_subuh, norm_azan
    # retrieving current time
    current_time = datetime.now().strftime("%I:%M %p")

    # playing azan subuh
    if current_time == prayer_times[0]:
        mixer.music.load(azan_subuh)
        mixer.music.play()
        time.sleep(3600)

    # playing normal azan
    elif current_time in prayer_times[2:6]:
        mixer.music.load(norm_azan)
        mixer.music.play()
        time.sleep(3600)

when both conditions are False. time.sleep(3600) is added because prayer times are at least one hour apart. I also realized when time.sleep(3600) is running, the program only uses 0.3% of CPU which narrowed down the suspect that hogs CPU usage to the while loop above.

How do I optimize my program further so that it won't use CPU that much?

Here is the whole script. If there are any suggestions on improving the program in other parts of the code, please feel free and comment down as it would help me a lot.

from datetime import datetime  # for time functionality
from bs4 import BeautifulSoup  # for web-parsing functionality
from pygame import mixer  # for mp3 compatibility
import requests
import time

# for saving all prayer times on that particular day
prayer_times = list()

# defining and initializing global vars for azan locations
azan_subuh = "c:/Users/AmmarFMR/Music/Azan/azan_subuh.mp3"
norm_azan = "c:/Users/AmmarFMR/Music/Azan/norm_azan.mp3"


def parser():
    # getting html file from website & parsing it
    source = requests.get("https://www.islamicfinder.org/world/malaysia/1735150/rawang-prayer-times/").text
    html = BeautifulSoup(source, "lxml")

    global prayer_times
    # getting the prayer times
    prayer_times = html.find_all("div", class_="todayPrayerDetailContainer")

    # cleaning up prayer_times list
for n in range(len(prayer_times)):
    prayer_times[n] = prayer_times[n].text.split("\n")[1]


def main():
    global prayer_times

    while True:
        global azan_subuh, norm_azan
        # retrieving current time
        current_time = datetime.now().strftime("%I:%M %p")

        # playing azan subuh
        if current_time == prayer_times[0]:
            mixer.music.load(azan_subuh)
            mixer.music.play()
            time.sleep(3600)

        # playing normal azan
        elif current_time in prayer_times[2:6]:
            mixer.music.load(norm_azan)
            mixer.music.play()
            time.sleep(3600)


# for the execution of the program
if __name__ == "__main__":
    mixer.init()
    parser()
    main()
else:
    print("This program is not meant to be used by other scripts.")
    time.sleep(3)
    exit(2)
  • have you considered using `cron` to trigger this at those times only instead of script being continuously checking it. Something like: https://stackoverflow.com/questions/373335/how-do-i-get-a-cron-like-scheduler-in-python – warl0ck Dec 28 '17 at 10:04

1 Answers1

1

Your script is consuming CPU time because most of the time it's constantly checking whether it's one of the special times; there's no delay between checks, so it's what's called a "busy loop". A simple sleep of less than one second in your main loop should fix that, so it checks only a few times a second instead of whenever it finishes:

while True:

    time.sleep(0.1)  # Avoid unnecessary checking

    global azan_subuh, norm_azan
    # retrieving current time
    current_time = datetime.now().strftime("%I:%M %p")

    # playing azan subuh
    if current_time == prayer_times[0]:
        mixer.music.load(azan_subuh)
        mixer.music.play()
        time.sleep(3600)

    # playing normal azan
    elif current_time in prayer_times[2:6]:
        mixer.music.load(norm_azan)
        mixer.music.play()
        time.sleep(3600)
  • I'd use this method as well, but now IT might be possible to miss the exact second. If you check for the desired time +/- 1 second you should be safe. – Jasper Dec 28 '17 at 10:09
  • You can also use a smaller timeframe; I just tested CPU usage for a busy loop (while True: pass) vs a 0.001 second wait (while True: time.sleep(0.001) and difference was 100% usage on one core to <1%. I'll update the answer. – Pedro von Hertwig Batista Dec 28 '17 at 10:11
  • Usually used is 0.001 secs. You put sleep as big as your check should be. If you do not need often checks, put 5 minutes or whatever. When a program is in sleep mode, kernel will check it at its leasure and activate it when the sleep timeout passed. That's why no CPU will be used while your program sleeps. BTW, putting sleep on top of a loop in this case is wrong. Add an else block and sleep only if neither of prior checks is right. – Dalen Dec 28 '17 at 10:47
  • Oh, and, how comes Python didn't make any noise about global statement being inside a loop, because this is complete non-sense. Once a variable is global, there is no need to turn it over and over again. The global statement should be on top of a function, for code readability and compatibility with Py 2.x. – Dalen Dec 28 '17 at 10:52