/*
* %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%
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "sys.h"
#include "global.h"
#include "benchmark.h"
#ifdef BM_COUNTERS
unsigned long long processes_busy;
unsigned long long processes_spawned;
unsigned long long messages_sent;
unsigned long long messages_copied;
unsigned long long messages_ego;
unsigned long long minor_gc;
unsigned long long major_gc;
#endif /* BM_COUNTERS */
#ifdef BM_TIMERS
#if (defined(__i386__) || defined(__x86_64__)) && USE_PERFCTR
#include "libperfctr.h"
struct vperfctr *system_clock;
double cpu_khz;
BM_NEW_TIMER(start);
static double get_hrvtime(void)
{
unsigned long long ticks;
double milli_seconds;
ticks = vperfctr_read_tsc(system_clock);
milli_seconds = (double)ticks / cpu_khz;
return milli_seconds;
}
static void stop_hrvtime(void)
{
if(system_clock)
{
vperfctr_stop(system_clock);
vperfctr_close(system_clock);
system_clock = NULL;
}
}
#else /* not perfctr, asuming Solaris */
#include <time.h>
BM_TIMER_T system_clock;
#endif
unsigned long local_pause_times[MAX_PAUSE_TIME];
unsigned long pause_times[MAX_PAUSE_TIME];
unsigned long pause_times_old[MAX_PAUSE_TIME];
BM_TIMER_T mmu;
BM_TIMER_T mmu_counter;
BM_NEW_TIMER(timer);
BM_NEW_TIMER(system);
BM_NEW_TIMER(gc);
BM_NEW_TIMER(minor_gc);
BM_NEW_TIMER(major_gc);
BM_NEW_TIMER(minor_global_gc);
BM_NEW_TIMER(major_global_gc);
BM_NEW_TIMER(send);
BM_NEW_TIMER(copy);
BM_NEW_TIMER(size);
BM_NEW_TIMER(max_minor);
BM_NEW_TIMER(max_major);
BM_NEW_TIMER(max_global_minor);
BM_NEW_TIMER(max_global_major);
BM_NEW_TIMER(misc0);
BM_NEW_TIMER(misc1);
BM_NEW_TIMER(misc2);
#endif /* BM_TIMERS */
#ifdef BM_HEAP_SIZES
unsigned long long max_used_heap;
unsigned long long max_allocated_heap;
unsigned long long max_used_global_heap;
unsigned long long max_allocated_global_heap;
#endif /* BM_HEAP_SIZES */
#ifdef BM_MESSAGE_SIZES
unsigned long long words_sent;
unsigned long long words_copied;
unsigned long long words_prealloc;
unsigned long long message_sizes[1000];
#endif /* BM_MESSAGE_SIZES */
/*****
* The following functions have to be defined, but they only have contents
* if certain keywords are defined.
*/
void init_benchmarking()
{
#ifdef BM_TIMERS
#if (defined(__i386__) || defined(__x86_64__)) && USE_PERFCTR
/* pass `--with-perfctr=/path/to/perfctr' when configuring */
struct perfctr_info info;
struct vperfctr_control control;
int i;
system_clock = vperfctr_open();
if (system_clock != NULL)
{
if (vperfctr_info(system_clock,&info) >= 0)
{
cpu_khz = (double)info.cpu_khz;
if (info.cpu_features & PERFCTR_FEATURE_RDTSC)
{
memset(&control,0,sizeof control);
control.cpu_control.tsc_on = 1;
}
}
if (vperfctr_control(system_clock,&control) < 0)
{
vperfctr_close(system_clock);
system_clock = NULL;
}
}
for (i = 0; i < 1000; i++)
{
BM_START_TIMER(system);
BM_STOP_TIMER(system);
}
timer_time = system_time / 1000;
start_time = 0;
#else
int i;
for (i = 0; i < 1000; i++)
{
BM_START_TIMER(system);
BM_STOP_TIMER(system);
}
timer_time = system_time / 1000;
#endif
for (i = 0; i < MAX_PAUSE_TIME; i++) {
local_pause_times[i] = 0;
pause_times[i] = 0;
pause_times_old[i] = 0;
}
mmu = 0;
mmu_counter = 0;
BM_MMU_INIT();
#endif /* BM_TIMERS */
#ifdef BM_COUNTERS
processes_busy = 0;
processes_spawned = 0;
messages_sent = 0;
messages_copied = 0;
messages_ego = 0;
minor_gc = 0;
major_gc = 0;
#endif /* BM_COUNTERS */
#ifdef BM_HEAP_SIZES
max_used_heap = 0;
max_allocated_heap = 0;
max_used_global_heap = 0;
max_allocated_global_heap = 0;
#endif /* BM_HEAP_SIZES */
#ifdef BM_MESSAGE_SIZES
words_sent = 0;
words_copied = 0;
words_prealloc = 0;
{
int i;
for (i = 0; i < 1000; i++)
message_sizes[i] = 0;
}
#endif /* BM_MESSAGE_SIZES */
}
void save_statistics()
{
#ifdef BM_STATISTICS
FILE *file = fopen(BM_STATISTICS_FILE,"a");
long i = 0;
if (file)
{
erts_fprintf(file,"-------------------------------------------------------------------------\n");
erts_fprintf(file,"The counters are reset at system start and are sums over the entire node.\n");
erts_fprintf(file,"You may reset them manually using the BIFs in the module hipe_bifs.\n");
erts_fprintf(file,"All times are given in milliseconds.\n");
erts_fprintf(file,"-------------------------------------------------------------------------\n");
erts_fprintf(file,"Node: %T\n",erts_this_node->sysname);
#ifdef BM_COUNTERS
erts_fprintf(file,"Number of processes spawned: %lld\n",processes_spawned);
erts_fprintf(file,"Number of local minor GCs: %lld\n",minor_gc);
erts_fprintf(file,"Number of local major GCs: %lld\n",major_gc);
erts_fprintf(file,"Number of messages sent: %lld\n",messages_sent);
erts_fprintf(file,"Number of messages copied: %lld\n",messages_copied);
erts_fprintf(file,"Number of messages sent to self: %lld\n",messages_ego);
#endif /* BM_COUNTERS */
#ifdef BM_MESSAGE_SIZES
erts_fprintf(file,"Number of words sent: %lld\n",words_sent);
erts_fprintf(file,"Number of words copied: %lld\n",words_copied);
erts_fprintf(file,"Number of words preallocated: %lld\n",words_prealloc);
#endif /* BM_MESSAGE_SIZES */
#ifdef BM_HEAP_SIZES
erts_fprintf(file,"Biggest local heap used (in words): %lld\n",max_used_heap);
erts_fprintf(file,"Biggest local heap allocated (in words): %lld\n",max_allocated_heap);
erts_fprintf(file,"Biggest global heap used (in words): %lld\n",max_used_global_heap);
erts_fprintf(file,"Biggest global heap allocated (in words): %lld\n",max_allocated_global_heap);
#endif /* BM_HEAP_SIZES */
#ifdef BM_TIMERS
erts_fprintf(file,"--- The total active system time is the sum of all times below ---\n");
BM_TIME_PRINTER("Mutator time",system_time);
BM_TIME_PRINTER("Time spent in send (excluding size & copy)",send_time);
BM_TIME_PRINTER("Time spent in size",size_time);
BM_TIME_PRINTER("Time spent in copy",copy_time);
BM_TIME_PRINTER("Time spent in local minor GC",minor_gc_time);
BM_TIME_PRINTER("Time spent in local major GC",major_gc_time);
BM_TIME_PRINTER("Time spent in global minor GC",minor_global_gc_time);
BM_TIME_PRINTER("Time spent in global major GC",major_global_gc_time);
erts_fprintf(file,"---\n");
BM_TIME_PRINTER("Maximum time spent in one separate local minor GC",max_minor_time);
BM_TIME_PRINTER("Maximum time spent in one separate local major GC",max_major_time);
BM_TIME_PRINTER("Maximum time spent in one separate global minor GC",max_global_minor_time);
BM_TIME_PRINTER("Maximum time spent in one separate global major GC",max_global_major_time);
#endif /* BM_TIMERS */
#if 0
/* Save a log file for import into excel */
long long total_time, n;
long left, right, mid;
#ifdef BM_COUNTERS
erts_fprintf(file,"Spawns\tLocalGC\tMAGC\tMessages\tMutator_t\tLocalGC_t\tMAGC_t\tLocMaxP\tLocMeanP\tLocGeoMP\tMAMaxP\tMAMeanP\tMAGeoMP\t\tCMAGC\tCMAGC_t\n");
erts_fprintf(file,"%lld\t%lld\t%lld\t%lld\t",
processes_spawned,
minor_garbage_cols + major_garbage_cols,
minor_global_garbage_cols + major_global_garbage_cols,
messages_sent);
#endif /* BM_COUNTERS */
#ifdef BM_TIMERS
erts_fprintf(file,"%lld\t%lld\t%lld\t",
(long long)(system_time + send_time + size_time + copy_time),
(long long)(minor_gc_time + major_gc_time),
(long long)(minor_global_gc_time + major_global_gc_time));
total_time = 0; n = 0;
left = 0; right = 0; mid = 0;
for (i = 0; i < MAX_PAUSE_TIME; i++) {
total_time += local_pause_times[i] * i;
n += local_pause_times[i];
if (i > mid)
right += local_pause_times[i];
while(right > left) {
left += local_pause_times[mid++];
right -= local_pause_times[mid];
}
}
erts_fprintf(file,"%lld\t%lld\t%ld\t",
(long long)((max_minor_time > max_major_time ?
max_minor_time :
max_major_time)*1000),
total_time / n,
mid);
total_time = 0; n = 0;
left = 0; right = 0; mid = 0;
for (i = 0; i < MAX_PAUSE_TIME; i++) {
if (pause_times[i] > 0) {
total_time += pause_times[i] * i;
n += pause_times[i];
if (i > mid)
right += pause_times[i];
while(right > left) {
left += pause_times[mid++];
right -= pause_times[mid];
}
}
}
erts_fprintf(file,"%lld\t%lld\t%ld\t",
(long long)((max_global_minor_time > max_global_major_time ?
max_global_minor_time :
max_global_major_time)*1000),
(n > 0 ? total_time / n : 0),
mid);
erts_fprintf(file,"\t%lld\t%lld\n",n,total_time);
erts_fprintf(file,"\nMinor:\n");
for (i = 0; i < MAX_PAUSE_TIME; i++) {
if (i < 1000 || pause_times[i] > 0) {
erts_fprintf(file,"%d\t%ld\n",i,pause_times[i]);
}
}
fprintf(file,"Major:\n");
for (i = 0; i < MAX_PAUSE_TIME; i++) {
if (pause_times_old[i] > 0) {
fprintf(file,"%d\t%ld\n",i,pause_times_old[i]);
}
}
#endif /* BM_TIMERS */
#ifdef BM_TIMERS
total_time = 0; n = 0;
left = 0; right = 0; mid = 0;
fprintf(file,"\nLocal:\n");
for (i = 0; i < MAX_PAUSE_TIME; i++) {
if (local_pause_times[i] > 0) {
erts_fprintf(file,"%d\t%ld\n",i,local_pause_times[i]);
total_time += local_pause_times[i] * i;
n += local_pause_times[i];
if (i > mid)
right += local_pause_times[i];
while(right > left) {
left += local_pause_times[mid++];
right -= local_pause_times[mid];
}
}
}
erts_fprintf(file,"Mid: %ld Mean: %ld\n",(long)mid,
(long)(n > 0 ? total_time / n : 0));
#endif
#endif /* 0 */
fclose(file);
}
else
fprintf(stderr,"Sorry... Can not write to %s!\n\r",BM_STATISTICS_FILE);
#endif /* BM_STATISTICS */
}