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
parent
99c1fda072
commit
ed7498bd4a
22
botany.py
22
botany.py
|
@ -4,11 +4,9 @@ import time
|
||||||
import pickle
|
import pickle
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import random
|
|
||||||
import getpass
|
import getpass
|
||||||
import threading
|
import threading
|
||||||
import errno
|
import errno
|
||||||
import uuid
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import menu_screen as ms
|
import menu_screen as ms
|
||||||
from plant import Plant
|
from plant import Plant
|
||||||
|
@ -16,7 +14,9 @@ from plant import Plant
|
||||||
# TODO:
|
# TODO:
|
||||||
# - switch from personal data file to row in DB
|
# - switch from personal data file to row in DB
|
||||||
# - is threading necessary?
|
# - is threading necessary?
|
||||||
# - reduce CPU usage
|
# - use a different curses window for plant, menu, info window, score
|
||||||
|
|
||||||
|
# notes from vilmibm
|
||||||
|
|
||||||
# there are threads.
|
# there are threads.
|
||||||
# - life thread. sleeps a variable amount of time based on generation bonus. increases tick count (ticks == score).
|
# - 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
|
# - exit
|
||||||
# quits program
|
# 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.
|
# 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:
|
# 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.
|
# - 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):
|
def start_threads(self,this_plant):
|
||||||
# creates threads to save files every minute
|
# 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 = threading.Thread(target=self.autosave, args=(this_plant,))
|
||||||
autosave_thread.daemon = True
|
autosave_thread.daemon = True
|
||||||
autosave_thread.start()
|
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):
|
def autosave(self, this_plant):
|
||||||
# running on thread, saves plant every 5s TODO: this is unnecessary
|
# running on thread, saves plant every 5s TODO: this is unnecessary
|
||||||
# and breaks shit probably
|
# and breaks shit probably
|
||||||
|
|
20
plant.py
20
plant.py
|
@ -3,6 +3,8 @@ import os
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import uuid
|
||||||
|
import getpass
|
||||||
|
|
||||||
class Plant:
|
class Plant:
|
||||||
# This is your plant!
|
# This is your plant!
|
||||||
|
@ -187,6 +189,8 @@ class Plant:
|
||||||
return rarity
|
return rarity
|
||||||
|
|
||||||
def dead_check(self):
|
def dead_check(self):
|
||||||
|
if self.dead:
|
||||||
|
return True
|
||||||
# if it has been >5 days since watering, sorry plant is dead :(
|
# if it has been >5 days since watering, sorry plant is dead :(
|
||||||
time_delta_watered = int(time.time()) - self.watered_timestamp
|
time_delta_watered = int(time.time()) - self.watered_timestamp
|
||||||
if time_delta_watered > (5 * (24 * 3600)):
|
if time_delta_watered > (5 * (24 * 3600)):
|
||||||
|
@ -327,10 +331,12 @@ class Plant:
|
||||||
|
|
||||||
def life(self):
|
def life(self):
|
||||||
# I've created life :)
|
# I've created life :)
|
||||||
|
generation_bonus = round(0.2 * (self.generation - 1), 1)
|
||||||
|
score_inc = 1 * (1 + generation_bonus)
|
||||||
while True:
|
while True:
|
||||||
if not self.dead:
|
if not self.dead:
|
||||||
if self.watered_24h:
|
if self.watered_24h:
|
||||||
self.ticks += 1
|
self.ticks += score_inc
|
||||||
if self.stage < len(self.stage_list)-1:
|
if self.stage < len(self.stage_list)-1:
|
||||||
if self.ticks >= self.life_stages[self.stage]:
|
if self.ticks >= self.life_stages[self.stage]:
|
||||||
self.growth()
|
self.growth()
|
||||||
|
@ -340,10 +346,10 @@ class Plant:
|
||||||
# Do something
|
# Do something
|
||||||
pass
|
pass
|
||||||
if self.dead_check():
|
if self.dead_check():
|
||||||
# Do something else
|
self.save_plant(this_plant)
|
||||||
pass
|
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
|
# TODO: event check
|
||||||
generation_bonus = round(0.2 * (self.generation - 1), 1)
|
time.sleep(2)
|
||||||
adjusted_sleep_time = 1 / (1 + generation_bonus)
|
|
||||||
time.sleep(adjusted_sleep_time)
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue