diff options
Diffstat (limited to 'erts/emulator/beam/benchmark.h')
-rw-r--r-- | erts/emulator/beam/benchmark.h | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/erts/emulator/beam/benchmark.h b/erts/emulator/beam/benchmark.h new file mode 100644 index 0000000000..eedb06a1b6 --- /dev/null +++ b/erts/emulator/beam/benchmark.h @@ -0,0 +1,340 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifndef __BENCHMARK_H__ +#define __BENCHMARK_H__ + +/* The define __BENCHMARK__ is the master switch to turn on and off + * benchmarking. This will enable the benchmark-BIFs in hipe_bif1.c. + * Documentation for the BIFs is in hipe_bif1.c, and that is where you + * will find the information about how to accually get some data out + * from these timers and counters. + */ +/* #define __BENCHMARK__ */ + +#ifdef __BENCHMARK__ +/* + * The defines below enables different parts of the benchmaring. + * Counters and timers that are disabled, always report zero in + * the BIFs. + */ + +/* BM_TIMERS keeps track of the time spent in diferent parts of the + * system. It only measures accual active time, not time spent in idle + * mode. These timers requires hardware support. For Linux, use the + * package perfctr from user.it.uu.se/~mikpe/linux/perfctr. If this + * package is not specified when configuring the system + * (--with-perfctr=PATH), the Solaris hrtime_t will be used. + * To add new timers look below. + */ +#define BM_TIMERS + +/* BM_COUNTERS count all kinds of events that occurs in the system. + * Among other things it counts the number of messages, then number of + * garbage collections, the number of processes spawned etc. + * To add new counters look below. + */ +#define BM_COUNTERS + +/* BM_MESSAGE_SIZES keeps a log of the size of all messages sent in + * the system. This introduce an overhead in time for the shared heap + * system since all message sizes have to be calculated at send. + */ +/* #define BM_MESSAGE_SIZES */ + +/* BM_HEAP_SIZES goes through all processes at garbage collection time + * to sum their allocated and used heap sizes. In anything else than a + * shared heap system, this will cost. + */ +/* #define BM_HEAP_SIZES */ + +/* BM_STATISTICS saves an entry in the file BM_STATISTICS_FILE. This + * is done for each erlang node at exit time. + */ +/* #define BM_STATISTICS */ + +#endif /* __BENCHMARK__ */ + + +#ifdef BM_STATISTICS +# define BM_STATISTICS_FILE "/tmp/erlang_statistics.joppe.log" +#endif /* BM_STATISTICS */ + + +/************ There are no more settings below this line *************/ + +/* + * Maintenance and how to add new stuff is documented by the code + * below ;-) + */ + +#ifdef BM_COUNTERS +/********************************************************************* + * To add new counters: + * + * Add the variable here AND in benchmark.c. Use the macro + * BM_COUNT(var) in the code where you want to increase it. + * + */ +extern unsigned long long processes_busy; +extern unsigned long long processes_spawned; +extern unsigned long long messages_sent; +extern unsigned long long messages_copied; +extern unsigned long long messages_ego; +extern unsigned long long minor_gc; +extern unsigned long long major_gc; +#ifdef HYBRID +extern unsigned long long minor_global_gc; +extern unsigned long long major_global_gc; +extern unsigned long long gc_in_copy; +#ifdef INCREMENTAL +extern unsigned long long minor_gc_cycles; +extern unsigned long long major_gc_cycles; +extern unsigned long long minor_gc_stages; +extern unsigned long long major_gc_stages; +#endif +#endif + +#define BM_COUNT(var) (var)++; + +#define BM_EGO_COUNT(send,rec) { \ + if ((send) == (rec)) \ + BM_COUNT(messages_ego); } + +#define BM_LAZY_COPY_START long long gcs = minor_global_gc + major_global_gc; +#define BM_LAZY_COPY_STOP { gcs = (minor_global_gc + major_global_gc) - gcs; \ + if (gcs > gc_in_copy) gc_in_copy = gcs; } + +#else /* !BM_COUNTERS */ +# define BM_COUNT(var) +# define BM_EGO_COUNT(send,rec) +# define BM_LAZY_COPY_START +# define BM_LAZY_COPY_STOP +#endif /* BM_COUNTERS */ + + +#ifdef BM_TIMERS +/********************************************************************* + * To add new timers: + * + * Add the variable below using the form extern BM_TIMER_T blah_time. + * Also add them in benchmark.c using the macro NEW_TIMER(blah). Use + * the macro BM_SWAP_TIMER(from,blah) ... BM_SWAP_TIMER(blah,to) to + * start and stop the new timer. Note, that you have to know what + * timer is running at the place where you want to insert your new + * timer to be able to stop and start (from,to) it. + * + * You can use the macros BM_STOP_TIMER(blah) and BM_START_TIMER(blah) + * around code that should not be timed at all. As above, you have to + * know what timer to start and stop. The system timer is running at + * most places in the emulator. Only the garbage collector and the + * message sending has its own timers at the moment. + * + * The timer_time used when stopping timers is the time it takes to + * start and stop the timers, calculated in init_benchmarking(). If it + * is not there, the time it takes to do this will accually be + * substantial compared to some small times in the system we want to + * meassure (send time in shared heap for instance). + */ + +#if (defined(__i386__) || defined(__x86_64__)) && USE_PERFCTR +#include "libperfctr.h" + +#define BM_TIMER_T double + +extern struct vperfctr *system_clock; +extern double cpu_khz; +extern BM_TIMER_T start_time; + +#define BM_START_TIMER(t) start_time = \ + (BM_TIMER_T)vperfctr_read_tsc(system_clock) / \ + cpu_khz; + +#define BM_STOP_TIMER(t) do { \ + BM_TIMER_T tmp = ((BM_TIMER_T)vperfctr_read_tsc(system_clock) / cpu_khz); \ + tmp -= (start_time + timer_time); \ + t##_time += (tmp > 0 ? tmp : 0); \ +} while(0) + +#define BM_TIME_PRINTER(str,time) do { \ + int min,sec,milli,micro; \ + BM_TIMER_T tmp = (time) * 1000; \ + micro = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ + tmp /= 1000; \ + milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ + tmp /= 1000; \ + sec = (uint)(tmp - ((int)(tmp / 60)) * 60); \ + min = (uint)tmp / 60; \ + erts_fprintf(file,str": %d:%02d.%03d %03d\n",min,sec,milli,micro); \ +} while(0) + +#else /* !USE_PERFCTR (Assuming Solaris) */ + +#define BM_TIMER_T hrtime_t +#define BM_START_TIMER(t) system_clock = sys_gethrtime() +#define BM_STOP_TIMER(t) do { \ + BM_TIMER_T tmp = (sys_gethrtime() - system_clock) - timer_time; \ + t##_time += (tmp > 0 ? tmp : 0); \ +} while(0) + +#define BM_TIME_PRINTER(str,time) do { \ + int min,sec,milli,micro; \ + BM_TIMER_T tmp; \ + tmp = (time) / 1000; \ + micro = tmp % 1000; \ + tmp /= 1000; \ + milli = tmp % 1000; \ + tmp /= 1000; \ + sec = tmp % 60; \ + min = tmp / 60; \ + erts_fprintf(file,str": %d:%02d.%03d %03d\n",min,sec,milli,micro); \ +} while(0) + +extern BM_TIMER_T system_clock; +#endif /* USE_PERFCTR */ + +extern BM_TIMER_T timer_time; +extern BM_TIMER_T system_time; +extern BM_TIMER_T gc_time; +extern BM_TIMER_T minor_gc_time; +extern BM_TIMER_T major_gc_time; +extern BM_TIMER_T minor_global_gc_time; +extern BM_TIMER_T major_global_gc_time; +extern BM_TIMER_T send_time; +extern BM_TIMER_T copy_time; +extern BM_TIMER_T size_time; +extern BM_TIMER_T max_minor_time; +extern BM_TIMER_T max_major_time; +extern BM_TIMER_T max_global_minor_time; +extern BM_TIMER_T max_global_major_time; +extern BM_TIMER_T misc0_time; +extern BM_TIMER_T misc1_time; +extern BM_TIMER_T misc2_time; + +#define MAX_PAUSE_TIME 500000 +extern unsigned long local_pause_times[MAX_PAUSE_TIME]; +extern unsigned long pause_times[MAX_PAUSE_TIME]; +extern unsigned long pause_times_old[MAX_PAUSE_TIME]; + +#define MMU_INTERVAL 5 /* milli seconds */ +extern BM_TIMER_T mmu_counter; +extern BM_TIMER_T mmu; + +#define BM_NEW_TIMER(t) BM_TIMER_T t##_time = 0; +#define BM_RESET_TIMER(t) t##_time = 0; +#define BM_SWAP_TIMER(t1,t2) do { BM_STOP_TIMER(t1); BM_START_TIMER(t2); } while(0) +#define BM_MMU_INIT() do { \ + BM_TIMER_T gc = gc_time; \ + while (gc > 0) { \ + if (gc > MMU_INTERVAL) { \ + gc -= MMU_INTERVAL - mmu_counter; \ + erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ + mmu_counter = 0; mmu = 0; \ + } else { \ + mmu_counter += gc; \ + if (mmu_counter >= MMU_INTERVAL) { \ + mmu_counter -= MMU_INTERVAL; \ + erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ + mmu = 0; \ + } \ + gc = 0; \ + } \ + } \ + BM_RESET_TIMER(system); \ + BM_RESET_TIMER(send); \ + BM_RESET_TIMER(copy); \ + BM_RESET_TIMER(size); \ +} while(0) + +#define BM_MMU_READ() do { \ + BM_TIMER_T mut = system_time + send_time + copy_time + size_time; \ + while (mut > 0) { \ + if (mut > MMU_INTERVAL) { \ + BM_TIMER_T tmp = MMU_INTERVAL - mmu_counter; \ + mmu += tmp; mut -= tmp; \ + erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ + mmu_counter = 0; mmu = 0; \ + } else { \ + mmu_counter += mut; mmu += mut; \ + if (mmu_counter >= MMU_INTERVAL) { \ + mmu_counter -= MMU_INTERVAL; \ + mmu -= mmu_counter; \ + erts_printf("%d\n",(int)((mmu / MMU_INTERVAL) * 100)); \ + mmu = mmu_counter; \ + } \ + mut = 0; \ + } \ + } \ +} while(0) + +#else /* !BM_TIMERS */ +# define BM_NEW_TIMER(t) +# define BM_START_TIMER(t) +# define BM_STOP_TIMER(t) +# define BM_RESET_TIMER(t) +# define BM_SWAP_TIMER(t1,t2) +# define BM_TIME_PRINTER(str,time) +# define BM_MMU_INIT() +# define BM_MMU_READ() +#endif /* BM_TIMERS */ + +#ifdef BM_HEAP_SIZES +extern unsigned long long max_used_heap; +extern unsigned long long max_allocated_heap; +extern unsigned long long max_used_global_heap; +extern unsigned long long max_allocated_global_heap; +#endif /* BM_HEAP_SIZES */ + +#ifdef BM_MESSAGE_SIZES +extern unsigned long long words_sent; +extern unsigned long long words_copied; +extern unsigned long long words_prealloc; +extern unsigned long long message_sizes[1000]; + +#define BM_MESSAGE_COPIED(size) { \ + words_copied += size; \ + BM_COUNT(messages_copied); } + +#define BM_PREALLOC_DATA(size) { \ + words_prealloc += size; } + +#define BM_MESSAGE(mess,send,rec) { \ + Uint msize = size_object(mess); \ + words_sent += msize; \ + if (msize < 1000) \ + message_sizes[msize]++; \ + else \ + message_sizes[999]++; \ + BM_EGO_COUNT(send,rec); \ + BM_COUNT(messages_sent); } + +#else /* !BM_MESSAGE_SIZES */ + +#define BM_MESSAGE_COPIED(size) BM_COUNT(messages_copied); +#define BM_PREALLOC_DATA(size) +#define BM_MESSAGE(mess,send,rec) { \ + BM_EGO_COUNT(send,rec); \ + BM_COUNT(messages_sent); } + +#endif /* BM_MESSAGE_SIZES */ + +void init_benchmarking(void); +void save_statistics(void); + +#endif /* _BENCHMARK_H_ */ |