def dump_object_scores(_connection=None): object_scores = defaultdict(Counter) (on_lot_objects, off_lot_objects) = _score_all_objects(object_scores) def _score_objects_callback(file, verbose=False): file.write('Object,total,On Lot,Off Lot') if verbose: file.write( ',Interaction,Autonomous,provided posture,state component,client change tuning,parts,stats,commodities,portal,inventory item, inventory, Object, Time Spent Adding, Time Spent Loading, Adds, Loads, Avg Load Time\n' ) else: file.write('\n') for object_type in object_scores: _dump_object_to_file(object_type, object_scores, on_lot_objects, off_lot_objects, file, verbose=verbose) _dump_total_score(object_scores, on_lot_objects, off_lot_objects, file) create_csv( 'object_scores_verbose', lambda new_file: _score_objects_callback(new_file, verbose=True), _connection) create_csv('object_scores_simple', _score_objects_callback, _connection)
def dump_tests_profile(sort:SortStyle=SortStyle.AVERAGE_TIME, _connection=None): output = sims4.commands.CheatOutput(_connection) if event_testing.resolver.test_profile is None: output('Test profiling is currently disabled. Use |performance.test_profile.enable') return if len(event_testing.resolver.test_profile) == 0: output('Test profiling is currently enabled but has no records.') return def sort_style(metric): if sort == SortStyle.AVERAGE_TIME: return metric.average_time if sort == SortStyle.TOTAL_TIME: return metric.total_time return metric.count def callback(file): TIME_MULTIPLIER = 1000 file.write('Test,Count,AverageTime(ms),TotalTime(ms),Resolver,Key,Count,AverageTime(ms),TotalTime(ms)\n') for (test_name, test_metrics) in sorted(event_testing.resolver.test_profile.items(), key=lambda t: sort_style(t[1].metrics), reverse=True): file.write('{},{},{},{},,,,,\n'.format(test_name, test_metrics.metrics.count, test_metrics.metrics.average_time*TIME_MULTIPLIER, test_metrics.metrics.total_time*TIME_MULTIPLIER)) for resolver in sorted(test_metrics.resolvers.keys()): data = test_metrics.resolvers[resolver] for (key, metrics) in sorted(data.items(), key=lambda t: sort_style(t[1]), reverse=True): while metrics.average_time > 0: file.write(',,,,{},{},{},{},{}\n'.format(resolver, key, metrics.count, metrics.average_time*TIME_MULTIPLIER, metrics.total_time*TIME_MULTIPLIER)) create_csv('test_profile', callback=callback, connection=_connection)
def mem_garbage_dump(_connection=None): old_flags = gc.get_debug() gc.set_debug(gc.DEBUG_SAVEALL) gc.collect() def write_to_file(file): garbage_ids = set(id(i) for i in gc.garbage) for o in gc.garbage: referents = tuple(r for r in gc.get_referents(o) if id(r) in garbage_ids) try: orepr = repr(o) except: orepr = '<exc>' file.write('{};{};{}{};{}\n'.format( len(referents), id(o), ''.join('{};'.format(id(r)) for r in referents), type(o).__name__, orepr)) output = sims4.commands.CheatOutput(_connection) output('Garbage count: {}'.format(len(gc.garbage))) filename = 'garbage_graph' create_csv(filename, callback=write_to_file, connection=_connection) gc.garbage.clear() gc.set_debug(old_flags)
def print_exclusivity_options(_connection=None): def callback(file): file.write('Category,' + ','.join(cat.name for cat in BouncerExclusivityCategory) + '\n') for cat1 in BouncerExclusivityCategory: row = [cat1.name] for cat2 in BouncerExclusivityCategory: rule = Bouncer.are_mutually_exclusive(cat1, cat2) if rule is None: row.append('') elif rule[2] == BouncerExclusivityOption.NONE: row.append('<' if rule[0] == cat1 else '^') elif rule[ 2] == BouncerExclusivityOption.EXPECTATION_PREFERENCE: row.append('EP') elif rule[2] == BouncerExclusivityOption.ALREADY_ASSIGNED: row.append('AR') else: row.append('ERROR') file.write(','.join(row) + '\n') file.write('\n\n') file.write('Legend:') file.write(',<,left category trumps above category\n') file.write(',^,above category trumps left category\n') file.write(',EP,expectation preference\n') file.write(',AR,already assigned\n') file.write(',blank,coexist\n') create_csv('bouncer_exclusivity_options', callback=callback, connection=_connection)
def dump_object_load_times(_connection=None): if not indexed_manager.capture_load_times: return False def _object_load_time_callback(file): file.write('Object,AddTime,LoadTime,Adds,Loads\n') for (object_class, object_load_data) in object_load_times.items(): if not isinstance(object_class, str): file.write('{},{},{},{},{},{}\n'.format( object_class, object_load_data.time_spent_adding, object_load_data.time_spent_loading, object_load_data.adds, object_load_data.loads, (object_load_data.time_spent_adding + object_load_data.time_spent_loading) / object_load_data.adds if object_load_data.adds > 0 else '0')) time_adding = sum([ y.time_spent_adding for (x, y) in object_load_times.items() if not isinstance(x, str) ]) time_loading = sum([ y.time_spent_loading for (x, y) in object_load_times.items() if not isinstance(x, str) ]) file.write(',{},{}\n'.format(time_adding, time_loading)) if 'household' in object_load_times: file.write('Household,{}\n'.format(object_load_times['household'])) file.write('Posture Graph,{},'.format( object_load_times['posture_graph'])) file.write('Lot Load,{}\n'.format(object_load_times['lot_load'])) create_csv('object_load_times', _object_load_time_callback, _connection)
def autonomy_distance_estimates_log_file(_connection=None): affordance_times = autonomy.autonomy_modes.g_affordance_times if affordance_times is None: return def callback(file): for (delta, count, _, affordance) in reversed(sorted([(delta, count, id(affordance), affordance) for (affordance, (count, delta)) in affordance_times.items()])): file.write('{},{},{}\n'.format(affordance, count, delta)) create_csv('autonomy_distance_estimate_times', callback=callback, connection=_connection)
def gather_tick_metrics_stop(_connection=None): def callback(file): zone = services.current_zone() tick_data = zone.tick_data zone.stop_gathering_tick_metrics() file.write('ABSOLUTE TICKS,SIM NOW READABLE, SIM NOW TICKS,CLOCK SPEED ENUM,CLOCK SPEED MULTIPLIER,GAME TIME READABLE,GAME TIME TICKS,MULTIPLIER TYPE\n') for data in tick_data: file.write('{},{},{},{},{},{},{},{}\n'.format(data.absolute_ticks, data.sim_now, data.sim_now.absolute_ticks(), data.clock_speed, data.clock_speed_multiplier, data.game_time, data.game_time.absolute_ticks(), data.multiplier_type)) create_csv('tick_metrics', callback=callback, connection=_connection)
def create_interaction_view(): for (tname, tmetrics) in event_testing.resolver.test_profile.items(): interaction_resolver = tmetrics.resolvers.get( 'InteractionResolver') if interaction_resolver is None: continue for (interaction, metrics) in interaction_resolver.items(): interactions[interaction].append((tname, metrics)) filename = 'test_profile_interactions' create_csv(filename, callback=interaction_callback, connection=_connection)
def analyze_slots(verbose:bool=False, _connection=None): output = sims4.commands.CheatOutput(_connection) if verbose: output('Collecting GC') gc.collect() if verbose: output('Gathering objects') pending = collections.deque(gc.get_objects()) all_objects = {id(obj): obj for obj in pending} while pending: obj = pending.pop() referents = gc.get_referents(obj) for child in referents: if id(child) not in all_objects: all_objects[id(child)] = child pending.append(child) if verbose and not len(all_objects) % 1000000: output('...{} pending: {}'.format(len(all_objects), len(pending))) if verbose: output('Collating types') type_map = collections.defaultdict(list) for obj in all_objects.values(): tp = type(obj) type_map[tp.__module__ + '.' + tp.__qualname__].append(obj) del all_objects def write_to_file(file): file.write('Type,Count,Size,Each,SlotSize,SlotEach,Attribs,SlotSavings,SlotSavings(MB),__slots__\n') for type_name in sorted(type_map.keys()): objects = type_map[type_name] if not hasattr(objects[0], '__dict__'): continue size = 0 attribs = set() if isinstance(objects[0], tuple): for obj in objects: attribs |= set(str(name) for name in vars(obj)) size += sys.getsizeof(obj) else: for obj in objects: attribs |= set(str(name) for name in vars(obj)) size += sys.getsizeof(obj) + sys.getsizeof(obj.__dict__) inst_size = size/len(objects) slot_inst_size = _size_of_slots(len(attribs)) slot_size = slot_inst_size*len(objects) slot_savings = size - slot_size slots_string = str(tuple(attribs)).replace(',', '') if len(attribs) < 50 else '(...)' file.write('{},{},{},{:0.2f},{},{},{},{},{:0.2f},{}\n'.format(type_name, len(objects), size, inst_size, slot_size, slot_inst_size, len(attribs), slot_savings, slot_savings/1048576, slots_string)) filename = 'PyOpt_AnalyzeSlots' create_csv(filename, callback=write_to_file, connection=_connection)
def object_count_log_dump(_connection=None): output = sims4.commands.CheatOutput(_connection) if gc_object_counts is None: output( 'Object count logging is disabled. Enable with |mem.gc.object_count_log_start' ) return def callback(file): file.write('minutes,count,collected\n') for (count, timestamp, collected) in gc_object_counts: file.write('{},{},{}\n'.format(timestamp, count, collected)) create_csv('gc_object_counts', callback=callback, connection=_connection)
def dump(cls, connection=None): if cls._affordance_times is None: return affordance_times = cls._affordance_times._affordances if not affordance_times: return def callback(file): file.write('Interaction,Count,TotalTime(s),AvgTime(s),TotalDistTime(s),AvgDistTime(s),TotalTransitionTime(s),AvgTransitionTime(s),TotalCompatTime(s),AvgCompatTime(s), Dist %, Transition %, Compat %\n') affordances = [(total, dist, transition, compat, count, affordance) for (affordance, (count, total, dist, transition, compat)) in affordance_times.items()] for (total, dist, transition, compat, count, affordance) in sorted(affordances, reverse=True): percent_multipler = 100/total file.write('{},{},{:0.04f},{:0.04f},{:0.04f},{:0.04f},{:0.04f},{:0.04f},{:0.04f},{:0.04f},{:0.02f},{:0.02f},{:0.02f}\n'.format(affordance, count, total, total/count, dist, dist/count, transition, transition/count, compat, compat/count, dist*percent_multipler, transition*percent_multipler, compat*percent_multipler)) create_csv('autonomy_distance_estimate_times', callback=callback, connection=connection)
def autonomy_distance_estimates_log_file(_connection=None): affordance_times = autonomy.autonomy_modes.g_affordance_times if affordance_times is None: return def callback(file): for (delta, count, _, affordance) in reversed( sorted([(delta, count, id(affordance), affordance) for (affordance, (count, delta)) in affordance_times.items()])): file.write('{},{},{}\n'.format(affordance, count, delta)) create_csv('autonomy_distance_estimate_times', callback=callback, connection=_connection)
def gather_tick_metrics_stop(_connection=None): def callback(file): zone = services.current_zone() tick_data = zone.tick_data zone.stop_gathering_tick_metrics() file.write( 'ABSOLUTE TICKS,SIM NOW READABLE, SIM NOW TICKS,CLOCK SPEED ENUM,CLOCK SPEED MULTIPLIER,GAME TIME READABLE,GAME TIME TICKS,MULTIPLIER TYPE\n' ) for data in tick_data: file.write('{},{},{},{},{},{},{},{}\n'.format( data.absolute_ticks, data.sim_now, data.sim_now.absolute_ticks(), data.clock_speed, data.clock_speed_multiplier, data.game_time, data.game_time.absolute_ticks(), data.multiplier_type)) create_csv('tick_metrics', callback=callback, connection=_connection)
def create_sim_resolver_view(): for (tname, tmetrics) in event_testing.resolver.test_profile.items(): datum_prefix = 'SingleSimResolver:' sim_resolver = tmetrics.resolvers.get('SingleSimResolver') if sim_resolver is None: datum_prefix = 'DoubleSimResolver:' sim_resolver = tmetrics.resolvers.get('DoubleSimResolver') if sim_resolver is None: continue for (resolver_datum, metrics) in sim_resolver.items(): sim_resolvers[datum_prefix + resolver_datum].append( (tname, metrics)) filename = 'test_profile_sim_resolvers' create_csv(filename, callback=sim_resolver_callback, connection=_connection)
def dump_autonomy_profiling_data(_connection=None): output = sims4.commands.CheatOutput(_connection) if autonomy.autonomy_util.g_autonomy_profile_data is None: output( 'Autonomy profiling is currently disabled. Use |performance.autonomy_profile.enable' ) return if not autonomy.autonomy_util.g_autonomy_profile_data.autonomy_requests: output('Autonomy profiling is currently enabled but has no records.') return def callback(file): autonomy.autonomy_util.g_autonomy_profile_data.write_profiling_data_to_file( file) filename = 'autonomy_profile' create_csv(filename, callback=callback, connection=_connection)
def dump_tests_profile(sort: SortStyle = SortStyle.AVERAGE_TIME, _connection=None): output = sims4.commands.CheatOutput(_connection) if event_testing.resolver.test_profile is None: output( 'Test profiling is currently disabled. Use |performance.test_profile.enable' ) return if len(event_testing.resolver.test_profile) == 0: output('Test profiling is currently enabled but has no records.') return def sort_style(metric): if sort == SortStyle.AVERAGE_TIME: return metric.average_time if sort == SortStyle.TOTAL_TIME: return metric.total_time return metric.count def callback(file): TIME_MULTIPLIER = 1000 file.write( 'Test,Count,AverageTime(ms),TotalTime(ms),Resolver,Key,Count,AverageTime(ms),TotalTime(ms)\n' ) for (test_name, test_metrics) in sorted( event_testing.resolver.test_profile.items(), key=lambda t: sort_style(t[1].metrics), reverse=True): file.write('{},{},{},{},,,,,\n'.format( test_name, test_metrics.metrics.count, test_metrics.metrics.average_time * TIME_MULTIPLIER, test_metrics.metrics.total_time * TIME_MULTIPLIER)) for resolver in sorted(test_metrics.resolvers.keys()): data = test_metrics.resolvers[resolver] for (key, metrics) in sorted(data.items(), key=lambda t: sort_style(t[1]), reverse=True): while metrics.average_time > 0: file.write(',,,,{},{},{},{},{}\n'.format( resolver, key, metrics.count, metrics.average_time * TIME_MULTIPLIER, metrics.total_time * TIME_MULTIPLIER)) create_csv('test_profile', callback=callback, connection=_connection)
def zone_gc_count_log_dump(_connection=None): output = sims4.commands.CheatOutput(_connection) if zone.gc_count_log is None: output( 'Zone gc count logging is disabled. Enable with |mem.gc.zone_gc_count_log_start' ) return def callback(file): file.write('zone_id, count, time\n') for (zone_id, count, time) in zone.gc_count_log: file.write('{:016x},{},{}\n'.format(zone_id, count, time)) current_zone = services.current_zone() now = services.time_service().sim_now time_in_zone = now - current_zone._time_of_zone_spin_up minutes_in_zone = math.floor(time_in_zone.in_minutes()) file.write('{:016x},{},{}\n'.format(current_zone.id, current_zone._gc_full_count, minutes_in_zone)) create_csv('zone_gc_counts', callback=callback, connection=_connection)
def dump_test_resolver_profiles(_connection=None): TIME_MULTIPLIER = 1000 def average_time(time, count): if time == 0 or count == 0: return 0 return time * TIME_MULTIPLIER / count resolver_types = set() for (test_name, test_metrics) in event_testing.resolver.test_profile.items(): resolver_types |= test_metrics.resolvers.keys() resolver_types = list(resolver_types) resolver_types.sort() def callback(file): file.write('Test,Count,AvgTime,ResolveTime,TestTime,{}\n'.format( ','.join('{}Count,{}'.format(x, x) for x in resolver_types))) for (test_name, test_metrics) in event_testing.resolver.test_profile.items(): metrics = test_metrics.metrics file.write('{},{},{},{},{}'.format( test_name, metrics.count, average_time(metrics.get_total_time(), metrics.count), average_time(metrics.resolve_time, metrics.count), average_time(metrics.test_time, metrics.count))) for resolver_type in resolver_types: sub_metrics = test_metrics.resolvers.get(resolver_type) if sub_metrics is None: file.write(',,') else: count = sum(m.count for m in sub_metrics.values()) resolve_time = sum(m.resolve_time for m in sub_metrics.values()) file.write(',{},{}'.format( count, average_time(resolve_time, count))) file.write('\n') filename = 'test_resolver_profile' create_csv(filename, callback=callback, connection=_connection)
def create_test_view(sort_style): if sort_style != SortStyle.ALL: filename = 'test_profile_' + str(sort_style).replace('.', '_') create_csv(filename, callback=test_callback, connection=_connection)
def dump_relationship_object_information_per_sim(_connection=None): def callback(file): entries = [] active_rel_objs = 0 playable_rel_objs = 0 unplayed_rel_obj = 0 one_way_rel_obj = 0 for x in services.sim_info_manager().values(): total_rels = 0 household_rels = 0 rels_can_be_culled = 0 rels_decaying = 0 rels_target_unplayable = 0 family_rels = 0 rels_with_no_long_term_tracks = 0 rels_target_playable = 0 rels_target_active = 0 for relationship in x.relationship_tracker: total_rels += 1 decay_information = x.relationship_tracker.relationship_decay_metrics( relationship.get_other_sim_id(x.sim_id)) if decay_information is not None: (decay_enabled, _, possible_tracks_decaying, _) = decay_information if decay_enabled: rels_decaying += 1 elif possible_tracks_decaying == 0: rels_with_no_long_term_tracks += 1 target_sim_info = relationship.get_other_sim_info(x.sim_id) if target_sim_info is not None: if target_sim_info.household_id == x.household_id: household_rels += 1 elif any(bit.relationship_culling_prevention == RelationshipBitCullingPrevention. PLAYED_AND_UNPLAYED for bit in relationship._bits.values()): family_rels += 1 else: rels_can_be_culled += 1 if not target_sim_info.is_npc: rels_target_active += 1 elif target_sim_info.is_played_sim: rels_target_playable += 1 else: rels_target_unplayable += 1 if not (x.is_npc and target_sim_info.is_npc): active_rel_objs += 1 elif x.is_played_sim or target_sim_info.is_played_sim: playable_rel_objs += 1 else: unplayed_rel_obj += 1 else: one_way_rel_obj += 1 entries.append('a{},{},{},{},{},{},{},{},{},{},{},{},{}\n'.format( x.id, x.first_name, x.last_name, total_rels, rels_can_be_culled, household_rels, family_rels, rels_target_active, rels_target_playable, rels_target_unplayable, rels_decaying, rels_with_no_long_term_tracks)) total_rel_objects = active_rel_objs + playable_rel_objs + unplayed_rel_obj + one_way_rel_obj file.write('Metrics\n') file.write('#relationship python objs:,{}\n'.format(total_rel_objects)) file.write( '#relationships one way objects:,{}\n'.format(one_way_rel_obj)) file.write( '#relationships active objects:,{}\n'.format(active_rel_objs)) file.write( '#relationships played objects:,{}\n'.format(playable_rel_objs)) file.write( '#relationships unplayed objects:,{}\n\n'.format(unplayed_rel_obj)) file.write( 'SimID,FirstName,LastName,Played,RelationshipObjects,#Cullable,HouseholdConnected,BitPreventingCulling,WithActive,WithPlayable,WithUnplayayble,#Decaying,#NoLongTermTracks\n' ) for row_entry in entries: file.write(row_entry) create_csv('relationship_objects_per_sim', callback=callback, connection=_connection)