I put a bit of instrumentation into my "AI" script, and this piece is the one hyper-expensive part.
I got an *ENORMOUS* performance improvement by starting my function with a single scan through my_ships:
--- Load ship data into a temporary table as we wind up using this data *heavily*
drop table if exists my_ship_data;
create temp table my_ship_data (
id integer,
fleet_id integer ,
player_id integer ,
name character varying ,
last_action_tic integer ,
last_move_tic integer ,
last_living_tic integer ,
current_health integer ,
max_health integer ,
current_fuel integer ,
max_fuel integer ,
max_speed integer ,
range integer ,
attack integer ,
defense integer ,
engineering integer ,
prospecting integer ,
location_x integer ,
location_y integer ,
direction integer ,
speed integer ,
destination_x integer ,
destination_y integer ,
repair_priority integer ,
action text ,
action_target_id integer ,
location point ,
destination point ,
target_speed integer ,
target_direction integer);
insert into my_ship_data (id , fleet_id , player_id , name ,
last_action_tic , last_move_tic , last_living_tic , current_health ,
max_health , current_fuel , max_fuel , max_speed , range , attack ,
defense , engineering , prospecting , location_x , location_y ,
direction , speed , destination_x , destination_y , repair_priority ,
action , action_target_id , location , destination , target_speed ,
target_direction)
select id , fleet_id , player_id , name ,
last_action_tic , last_move_tic , last_living_tic , current_health ,
max_health , current_fuel , max_fuel , max_speed , range , attack ,
defense , engineering , prospecting , location_x , location_y ,
direction , speed , destination_x , destination_y , repair_priority ,
action , action_target_id , location , destination , target_speed ,
target_direction from my_ships;
create index msd_id on my_ship_data(id);
create index msd_fleet on my_ship_data(fleet_id);
create index msd_loc on my_ship_data using gist(location);
create index msd_dest on my_ship_data using gist(destination);
I'll observe that this call to my_ships typically takes 15-20 seconds, whereas the other steps tend to take fractions of a second apiece.
Here's typical, in production:
schem...@db.schemaverse.com-> explain analyze select * from my_ships;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=5517.19..5897.33 rows=762 width=188) (actual time=16290.601..16977.745 rows=108 loops=1)
Hash Cond: (ship_control.ship_id =
ship.id)
-> Seq Scan on ship_control (cost=0.00..320.83 rows=13783 width=83) (actual time=0.038..678.540 rows=13761 loops=1)
-> Hash (cost=5507.66..5507.66 rows=762 width=109) (actual time=16290.435..16290.435 rows=108 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 13kB
-> Seq Scan on ship (cost=0.00..5507.66 rows=762 width=109) (actual time=12914.614..16289.944 rows=108 loops=1)
Filter: ((NOT destroyed) AND (player_id = get_player_id("session_user"())))
Total runtime: 16977.971 ms
(8 rows)