Crossfire Server, Trunk
comet_perf.cpp
Go to the documentation of this file.
1 /*
2  * static char *rcsid_check_alchemy_c =
3  * "$Id: check_alchemy.c 4640 2006-06-07 21:44:18Z tchize $";
4  */
5 
6 /*
7  * CrossFire, A Multiplayer game for X-windows
8  *
9  * Copyright (C) 2007 Mark Wedel & Crossfire Development Team
10  * Copyright (C) 1992 Frank Tore Johansen
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  *
26  * The authors can be reached via e-mail at crossfire-devel@real-time.com
27  */
28 
29 /*
30  * This tests the comet spell. My main motivation for writing this
31  * was to have a consistent test I could use for performance testing.
32  * But I also wanted to make sure that the results were close before and
33  * after the performance changes - make the spell use less cpu time
34  * but having drastically different results probably isn't a good thing
35  * either.
36  * To really be useful, everything needs to be compiled with profiling
37  * (-pg). This can be done like 'setenv CFLAGS -pg; ./configure;
38  * make clean; make'. The make clean is necessary because it won't
39  * recompile the objects based just on changes the the CFLAGS.
40  *
41  * Note that this test, even after performance changes are done, still
42  * isn't bad - it checks several things - map creation, spell casting,
43  * etc. It wouldn't be hard to use this as a template to test things
44  * like resistance code, etc.
45  */
46 
47 #include <stdlib.h>
48 #include <check.h>
49 
50 #include <global.h>
51 #include <sproto.h>
52 #include <toolkit_common.h>
53 
54 #define TEST_MAP_SIZE 40
55 #define NUM_TICKS_TO_RUN 500
56 #define NUM_COMETS_TO_CAST 30
57 #define STARTING_HP 25000
58 
59 /* The percentage, either plus or minus, that the results can
60  * vary from the baseline and still be considered OK.
61  * Note a good sanity check to make sure you put in the correct
62  * values is to set this to 0.0 - in that case, checks should
63  * pass.
64  */
65 #define HP_DELTA 10
66 
67 /* The first time you set up a test, you want to dump the
68  * initial values to put into the hp_row/hp_diag arrays.
69  * If this is set, it prints those values instead of doing
70  * a comparision.
71  */
72 /*#define PRINT_DEBUG_HP */
73 
75 object *mon;
76 
77 static void setup(void) {
78  object *mon1;
79  int x, i;
80 
82 
83  mon = create_archetype("orc");
84  fail_unless(mon != NULL, "Unable to find orc object");
85 
86  /* We customize the monster a bit so it is consistent -
87  * give it a bunch of HP so it can survive the attacks,
88  * set it speed to 0 so it doesn't do anything, etc.
89  */
90  for (i = 0; i < NROFATTACKS; i++)
91  mon->resist[i] = 95;
94  mon->speed = 0.0;
95  mon->speed_left = 0.0;
98 
99  /* We now make copies of this custom monster and put it into the
100  * map. We make a diagonal from one corner to another,
101  * as well as a line across the middle of the map (\ overlayed with -)
102  * We could fill most of the map with monsters, but I think the
103  * diagonal + horizontal should give a pretty representative
104  * value of creatures being hit.
105  */
106  for (x = 0; x < TEST_MAP_SIZE; x++) {
107  mon1 = object_new();
108  object_copy(mon, mon1);
109  object_insert_in_map_at(mon1, test_map, NULL, 0, x, TEST_MAP_SIZE/2);
110 
111  if (x != TEST_MAP_SIZE/2) {
112  mon1 = object_new();
113  object_copy(mon, mon1);
114  object_insert_in_map_at(mon1, test_map, NULL, 0, x, x);
115  }
116  }
117 
118 }
119 
120 static void teardown(void) {
122 }
123 
124 static void check_hp(const char *test, int hp_row[TEST_MAP_SIZE], int hp_diag[TEST_MAP_SIZE]) {
125  object *our_mon;
126  int x, diff;
127 
128 #ifdef PRINT_DEBUG_HP
129  printf("\nDumping debug hp for test %s\n ", test);
130  (void)hp_row;
131  (void)hp_diag;
132  (void)diff;
133 #else
134  (void)test;
135 #endif
136 
137  /* Dump the HP of the monsters now. We do it in 2 passes,
138  * as I think it is easier to read that way.
139  */
140  for (x = 0; x < TEST_MAP_SIZE; x++) {
141  our_mon = GET_MAP_OB(test_map, x, TEST_MAP_SIZE/2);
142  if (!our_mon) {
143  fail("Monster destroyed at %d, %d\n", x, TEST_MAP_SIZE/2);
144  continue;
145  }
146  fail_unless(mon->name == our_mon->name, "Could not find our monster on the space?");
147 
148 #ifdef PRINT_DEBUG_HP
149  printf("%d, ", our_mon->stats.hp);
150 #else
151 
152  if (our_mon->stats.hp == hp_row[x]) {
153  diff = 0;
154  } else if (our_mon->stats.hp < hp_row[x]) {
155  diff = 100-(STARTING_HP-hp_row[x])*100/((STARTING_HP-our_mon->stats.hp) ? (STARTING_HP-our_mon->stats.hp) : 1);
156  } else {
157  diff = -(100-(STARTING_HP-our_mon->stats.hp)*100/((STARTING_HP-hp_row[x]) ? (STARTING_HP-hp_row[x]) : 1));
158  }
159 
160  if (FABS(diff) > HP_DELTA) {
161  fail("Mon (%d, %d) has hp out of range (%d != %d +/- %d, diff %d)\n", our_mon->x, our_mon->y, our_mon->stats.hp, hp_row[x], HP_DELTA, diff);
162  }
163 #endif
164  }
165 
166 #ifdef PRINT_DEBUG_HP
167  printf("\n\n");
168 #endif
169 
170  for (x = 0; x < TEST_MAP_SIZE; x++) {
171  our_mon = GET_MAP_OB(test_map, x, x);
172  if (!our_mon) {
173  fprintf(stderr, "Monster destroyed at %d, %d\n", x, x);
174  continue;
175  }
176 
177  fail_unless(mon->name == our_mon->name, "Could not find our monster on the space?");
178 
179 #ifdef PRINT_DEBUG_HP
180  printf("%d, ", our_mon->stats.hp);
181 #else
182  if (our_mon->stats.hp == hp_diag[x]) {
183  diff = 0;
184  } else if (our_mon->stats.hp < hp_diag[x]) {
185  diff = 100-(STARTING_HP-hp_diag[x])*100/((STARTING_HP-our_mon->stats.hp) ? (STARTING_HP-our_mon->stats.hp) : 1);
186  } else {
187  diff = -(100-(STARTING_HP-our_mon->stats.hp)*100/((STARTING_HP-hp_diag[x]) ? (STARTING_HP-hp_diag[x]) : 1));
188  }
189 
190  if (FABS(diff) > HP_DELTA) {
191  fail("Mon (%d, %d) has hp out of range (%d != %d +/- %d, diff %d)\n", our_mon->x, our_mon->y, our_mon->stats.hp, hp_diag[x], HP_DELTA, diff);
192  }
193 #endif
194  }
195 }
196 
197 START_TEST(cast_one_comet) {
198  int hp_row[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 24924, 24920, 24916, 24912, 24908, 24904, 24900, 24896, 24892, 24888, 24884, 24880, 24869, 24880, 24884, 24888, 24892, 24896, 24900, 24904, 24908, 24912, 24916, 24920, 24924, 25000, 25000, 25000, 25000, 25000, 25000, 25000 },
199  hp_diag[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 24924, 24920, 24916, 24912, 24908, 24904, 24900, 24896, 24892, 24888, 24884, 24880, 24869, 24880, 24884, 24888, 24892, 24896, 24900, 24904, 24908, 24912, 24916, 24920, 24924, 25000, 25000, 25000, 25000, 25000, 25000, 25000 };
200  object *comet, *rod;
201  int tick;
202 
203  rod = create_archetype("rod_heavy");
204  rod->level = 100;
205  comet = create_archetype("spell_comet");
206  object_insert_in_ob(comet, rod);
207 
209 
210  cast_spell(rod, rod, 1, rod->inv, NULL);
211  for (tick = 0; tick < NUM_TICKS_TO_RUN; tick++) {
212  process_events();
213  }
214 
215  check_hp("cast_one_comet", hp_row, hp_diag);
216 }
217 END_TEST
218 
219 START_TEST(cast_random_comet) {
220  object *comet, *rod;
221  int tick, num_cast = 0;
222  int hp_row[TEST_MAP_SIZE] = {23756, 23617, 23516, 23428, 23397, 23291, 23203, 23097, 23014, 22875, 22801, 22782, 22706, 22707, 22620, 22645, 22646, 22595, 22705, 22773, 22809, 22835, 22975, 23098, 23239, 23346, 23462, 23597, 23627, 23675, 23786, 23888, 24001, 24119, 24206, 24306, 24336, 24455, 24565, 24649 },
223  hp_diag[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 23515, 23351, 23177, 23097, 22946, 22931, 22763, 22706, 22678, 22658, 22728, 22812, 22809, 22712, 22728, 22741, 22726, 22833, 22862, 22967, 23014, 23009, 23167, 23267, 23367, 23459, 23596, 23713, 23750, 23879, 24026, 24160 };
224 
225  rod = create_archetype("rod_heavy");
226  rod->level = 100;
227  comet = create_archetype("spell_comet");
228  object_insert_in_ob(comet, rod);
229 
231 
232  for (tick = 0; tick < NUM_TICKS_TO_RUN; tick++) {
233  if (num_cast < NUM_COMETS_TO_CAST && (tick%1) == 0) {
234  object_remove(rod);
235 
236  /* The idea here on the x is to shuffle the spaces around
237  * a little, as a more typical case is comets
238  * blowing up on different spaces.
239  */
240  object_insert_in_map_at(rod, test_map, NULL, 0, (tick*59)%37, TEST_MAP_SIZE-1);
241 
242  cast_spell(rod, rod, 1, rod->inv, NULL);
243  num_cast++;
244  }
245  process_events();
246  }
247  check_hp("cast_random_comet", hp_row, hp_diag);
248 
249 }
250 END_TEST
251 
252 START_TEST(cast_bunch_comet) {
253  object *comet, *rod;
254  int tick, num_cast = 0;
255  int hp_row[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 22355, 22262, 22115, 21966, 21837, 21684, 21554, 21424, 21268, 21137, 21006, 20875, 20534, 20875, 21006, 21137, 21268, 21424, 21554, 21684, 21837, 21966, 22115, 22262, 22355, 25000, 25000, 25000, 25000, 25000, 25000, 25000 },
256  hp_diag[TEST_MAP_SIZE] = {25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 22355, 22262, 22115, 21966, 21837, 21684, 21554, 21424, 21268, 21137, 21006, 20875, 20534, 20875, 21006, 21137, 21268, 21424, 21554, 21684, 21837, 21966, 22115, 22262, 22355, 25000, 25000, 25000, 25000, 25000, 25000, 25000 };
257 
258  rod = create_archetype("rod_heavy");
259  rod->level = 100;
260  comet = create_archetype("spell_comet");
261  object_insert_in_ob(comet, rod);
262 
264 
265  for (tick = 0; tick < NUM_TICKS_TO_RUN; tick++) {
266  if (num_cast < NUM_COMETS_TO_CAST && (tick%1) == 0) {
267  cast_spell(rod, rod, 1, rod->inv, NULL);
268  num_cast++;
269  }
270  process_events();
271  }
272  check_hp("cast_bunch_comet", hp_row, hp_diag);
273 
274 }
275 END_TEST
276 
277 static Suite *comet_suite(void) {
278  Suite *s = suite_create("comet");
279  TCase *tc_core = tcase_create("Core");
280 
281  /* check by defaults has a 2 second timeout - that isn't
282  * fast enough on my system - a run of 30 comets takes about
283  * 7 seconds. Setting this to 20 is enough, but on a slower
284  * system may not be.
285  */
286  tcase_set_timeout(tc_core, 20);
287 
288  /*setup and teardown will be called before each test in testcase 'tc_core' */
289  tcase_add_checked_fixture(tc_core, setup, teardown);
290 
291  suite_add_tcase(s, tc_core);
292  tcase_add_test(tc_core, cast_one_comet);
293  tcase_add_test(tc_core, cast_bunch_comet);
294  tcase_add_test(tc_core, cast_random_comet);
295 
296  return s;
297 }
298 
299 int main(void) {
300  int nf;
301 
302  Suite *s = comet_suite();
303  SRunner *sr = srunner_create(s);
304 
305  /* Don't want to fork - if we do, we lose the profile (-pg)
306  * compiled information, which is what I used to determine if
307  * things are more efficient.
308  */
309  srunner_set_fork_status(sr, CK_NOFORK);
310 
311  /* Only want to run this once, so don't put it in setup() */
312  cctk_setdatadir(SOURCE_ROOT "lib");
313  init(0, NULL);
314 
315  srunner_set_xml(sr, LOGDIR "/unit/server/comet.xml");
316  srunner_set_log(sr, LOGDIR "/unit/server/comet.out");
317  srunner_run_all(sr, CK_ENV); /*verbosity from env variable*/
318  nf = srunner_ntests_failed(sr);
319  srunner_free(sr);
320  fprintf(stderr, "Got %" FMT64U " supressions, %" FMT64U " spell merges, %" FMT64U " full tables\n", statistics.spell_suppressions, statistics.spell_merges, statistics.spell_hash_full);
321  return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
322 }
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:171
global.h
living::maxhp
int16_t maxhp
Definition: living.h:41
START_TEST
START_TEST(cast_one_comet)
Definition: comet_perf.cpp:197
FLAG_STAND_STILL
#define FLAG_STAND_STILL
Definition: define.h:308
statistics
struct Statistics statistics
Definition: init.cpp:232
FABS
#define FABS(x)
Definition: define.h:22
get_empty_map
mapstruct * get_empty_map(int sizex, int sizey)
Definition: map.cpp:842
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
test_map
mapstruct * test_map
Definition: comet_perf.cpp:74
object::inv
object * inv
Definition: object.h:298
diamondslots.x
x
Definition: diamondslots.py:15
cctk_setdatadir
void cctk_setdatadir(const char *datadir)
Definition: toolkit_common.cpp:69
cast_spell
int cast_spell(object *op, object *caster, int dir, object *spell_ob, char *stringarg)
Definition: spell_util.cpp:1424
object::speed
float speed
Definition: object.h:337
object::x
int16_t x
Definition: object.h:335
object::speed_left
float speed_left
Definition: object.h:338
mon
object * mon
Definition: comet_perf.cpp:75
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.cpp:1192
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
object::level
int16_t level
Definition: object.h:361
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
object::resist
int16_t resist[NROFATTACKS]
Definition: object.h:351
object::y
int16_t y
Definition: object.h:335
toolkit_common.h
setup
static void setup(void)
Definition: comet_perf.cpp:77
process_events
void process_events(void)
Definition: server.cpp:1085
Statistics::spell_hash_full
uint64_t spell_hash_full
Definition: global.h:363
HP_DELTA
#define HP_DELTA
Definition: comet_perf.cpp:65
object_update_speed
void object_update_speed(object *op)
Definition: object.cpp:1344
Statistics::spell_suppressions
uint64_t spell_suppressions
Definition: global.h:364
TEST_MAP_SIZE
#define TEST_MAP_SIZE
Definition: comet_perf.cpp:54
init
pluglist shows those as well as a short text describing each the list will simply appear empty The keyword for the Python plugin is Python plugout< keyword > Unloads a given identified by its _keyword_ So if you want to unload the Python you need to do plugout Python plugin< libname > Loads a given whose _filename_ is libname So in the case of you d have to do a plugin cfpython so Note that all filenames are relative to the default plugin it tries to load all available files in the SHARE plugins directory as plugin libraries It first displays the Initializing the plugin has the opportunity to signal itself by a message on the console Then the server displays an informative message containing both the plugin content and its keyword For the Python the standard load process thus GreenGoblin When a plugin has been it can request to be warned whenever a global event and are named freely by the developer If the directory doesn t nothing will happen< event name > can be init
Definition: plugins.txt:54
check_hp
static void check_hp(const char *test, int hp_row[TEST_MAP_SIZE], int hp_diag[TEST_MAP_SIZE])
Definition: comet_perf.cpp:124
free_map
void free_map(mapstruct *m)
Definition: map.cpp:1650
NUM_TICKS_TO_RUN
#define NUM_TICKS_TO_RUN
Definition: comet_perf.cpp:55
sproto.h
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.cpp:2095
object_new
object * object_new(void)
Definition: object.cpp:1268
teardown
static void teardown(void)
Definition: comet_perf.cpp:120
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
object::name
sstring name
Definition: object.h:319
STARTING_HP
#define STARTING_HP
Definition: comet_perf.cpp:57
mapstruct
Definition: map.h:314
Statistics::spell_merges
uint64_t spell_merges
Definition: global.h:362
comet_suite
static END_TEST Suite * comet_suite(void)
Definition: comet_perf.cpp:277
FMT64U
#define FMT64U
Definition: compat.h:17
object_remove
void object_remove(object *op)
Definition: object.cpp:1828
object::stats
living stats
Definition: object.h:378
NUM_COMETS_TO_CAST
#define NUM_COMETS_TO_CAST
Definition: comet_perf.cpp:56
main
int main(void)
Definition: comet_perf.cpp:299
living::hp
int16_t hp
Definition: living.h:40