reduce CPU usage dramatically

I noticed that on tilde.town users with a high botany score used up a
lot of CPU cycles. I skimmed through the code and didn't immediately see
any tight loops, but after profiling against a user's borrowed .botany
directory I saw the culprit: the score increase thread.

This thread was designed to increase the user's score by 1 every time
the thread did an iteration of its infinite loop. It would sleep for an
interval scaled *down* based on how high a user's generation bonus was.
This meant that the sleep interval trended towards zero, creating a
tight loop for high scoring users.

This commit changes the code to use a constant sleep inteveral but scale
the score increment *up* based on generation.

I also removed the death check thread entirely since we were already
checking for death in the score thread. I also short circuited the death
check.

This had the effect of reducing CPU load for a high scoring user by a
factor of about 50.
pull/53/head
Nate Smith 2023-12-06 20:56:11 -08:00
parent 99c1fda072
commit ed7498bd4a
2 changed files with 16 additions and 26 deletions

View File

@ -4,11 +4,9 @@ import time
import pickle
import json
import os
import random
import getpass
import threading
import errno
import uuid
import sqlite3
import menu_screen as ms
from plant import Plant
@ -16,7 +14,9 @@ from plant import Plant
# TODO:
# - switch from personal data file to row in DB
# - is threading necessary?
# - reduce CPU usage
# - use a different curses window for plant, menu, info window, score
# notes from vilmibm
# there are threads.
# - life thread. sleeps a variable amount of time based on generation bonus. increases tick count (ticks == score).
@ -41,7 +41,6 @@ from plant import Plant
# - exit
# quits program
# part of the complexity of all this is everything takes place in one curses window; thus, updates must be manually synchronized across the various logical parts of the screen.
# ideally, multiple windows would be used:
# - the menu. it doesn't change unless the plant dies OR the plant hits stage 5, then "harvest" is dynamically added.
@ -91,25 +90,10 @@ class DataManager(object):
def start_threads(self,this_plant):
# creates threads to save files every minute
death_check_thread = threading.Thread(target=self.death_check_update, args=(this_plant,))
death_check_thread.daemon = True
death_check_thread.start()
autosave_thread = threading.Thread(target=self.autosave, args=(this_plant,))
autosave_thread.daemon = True
autosave_thread.start()
def death_check_update(self,this_plant):
# .1 second updates and lock to minimize race condition
while True:
is_dead = this_plant.dead_check()
if is_dead:
self.save_plant(this_plant)
self.data_write_json(this_plant)
self.update_garden_db(this_plant)
self.harvest_plant(this_plant)
this_plant.unlock_new_creation()
time.sleep(.1)
def autosave(self, this_plant):
# running on thread, saves plant every 5s TODO: this is unnecessary
# and breaks shit probably

View File

@ -3,6 +3,8 @@ import os
import json
import threading
import time
import uuid
import getpass
class Plant:
# This is your plant!
@ -187,6 +189,8 @@ class Plant:
return rarity
def dead_check(self):
if self.dead:
return True
# if it has been >5 days since watering, sorry plant is dead :(
time_delta_watered = int(time.time()) - self.watered_timestamp
if time_delta_watered > (5 * (24 * 3600)):
@ -327,10 +331,12 @@ class Plant:
def life(self):
# I've created life :)
generation_bonus = round(0.2 * (self.generation - 1), 1)
score_inc = 1 * (1 + generation_bonus)
while True:
if not self.dead:
if self.watered_24h:
self.ticks += 1
self.ticks += score_inc
if self.stage < len(self.stage_list)-1:
if self.ticks >= self.life_stages[self.stage]:
self.growth()
@ -340,10 +346,10 @@ class Plant:
# Do something
pass
if self.dead_check():
# Do something else
pass
self.save_plant(this_plant)
self.data_write_json(this_plant)
self.update_garden_db(this_plant)
self.harvest_plant(this_plant)
this_plant.unlock_new_creation()
# TODO: event check
generation_bonus = round(0.2 * (self.generation - 1), 1)
adjusted_sleep_time = 1 / (1 + generation_bonus)
time.sleep(adjusted_sleep_time)
time.sleep(2)