blob: b86af528794c89ab004bdc2540d9d4fc1192d217 [file] [log] [blame]
/*
*Licensed to the Apache Software Foundation (ASF) under one
*or more contributor license agreements. See the NOTICE file
*distributed with this work for additional information
*regarding copyright ownership. The ASF licenses this file
*to you under the Apache License, Version 2.0 (the
*"License"); you may not use this file except in compliance
*with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing,
*software distributed under the License is distributed on an
*"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
*specific language governing permissions and limitations
*under the License.
*/
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
#include "benchmark.h"
static const char * const BENCHMARK_NAME = "INTR_CONT";
static const double SAMPLE_FACTOR = 100;
static const __useconds_t WAIT_TIME = 1; //100 * 1000;
typedef enum benchmark_state {
BENCHMARK_STATE_INTERRUPTED,
BENCHMARK_STATE_RUNNING
} benchmark_state_t;
struct benchmark {
int nrOfThreads;
pthread_mutex_t mutex; //write protect for state
math_service_pt math;
benchmark_state_t state;
int threadsRunning;
};
typedef struct thread_info {
benchmark_pt benchmark;
int nrOfSamples;
unsigned int result;
struct timeval begin;
struct timeval end;
int skips;
} thread_info_t;
static void benchmark_thread(thread_info_t *info);
static void benchmark_runSamples(thread_info_t *info, int *i, volatile benchmark_state_t *state);
static void benchmark_interrupt(benchmark_pt benchmark);
static void benchmark_continue(benchmark_pt benchmark);
celix_status_t benchmark_create(benchmark_pt *benchmark) {
(*benchmark) = malloc(sizeof(struct benchmark));
(*benchmark)->math = NULL;
(*benchmark)->state = BENCHMARK_STATE_INTERRUPTED;
(*benchmark)->nrOfThreads = 0;
(*benchmark)->threadsRunning = 0;
pthread_mutex_init(&(*benchmark)->mutex, NULL);
return CELIX_SUCCESS;
}
celix_status_t benchmark_destroy(benchmark_pt benchmark) {
free(benchmark);
return CELIX_SUCCESS;
}
benchmark_result_t benchmark_run(benchmark_pt benchmark, int nrOfThreads, int nrOfSamples) {
int i;
pthread_t threads[nrOfThreads];
thread_info_t infos[nrOfThreads];
int isThreadRunning[nrOfThreads];
benchmark_result_t result;
unsigned long elapsedTime = 0;
result.skips =0;
for (i = 0 ; i < nrOfThreads ; i += 1) {
infos[i].benchmark = benchmark;
infos[i].nrOfSamples = nrOfSamples;
infos[i].skips = 0;
infos[i].result = rand();
pthread_create(&threads[i], NULL, (void *)benchmark_thread, &infos[i]);
}
benchmark->nrOfThreads = nrOfThreads;
for (i = 0; i < nrOfThreads ; i += 1) {
pthread_join(threads[i], NULL);
elapsedTime += ((infos[i].end.tv_sec - infos[i].begin.tv_sec) * 1000000) + (infos[i].end.tv_usec - infos[i].begin.tv_usec);
result.skips += infos[i].skips;
}
benchmark->nrOfThreads = 0;
result.averageCallTimeInNanoseconds = elapsedTime;
result.averageCallTimeInNanoseconds *= 1000;
result.averageCallTimeInNanoseconds /= nrOfSamples;
result.averageCallTimeInNanoseconds /= nrOfThreads;
result.callFrequencyInMhz = ((double)(nrOfSamples * nrOfThreads) / elapsedTime);
result.nrOfThreads = nrOfThreads;
result.nrOfsamples = nrOfSamples;
return result;
}
static void benchmark_thread(thread_info_t *info) {
int i = 0;
gettimeofday(&info->begin, NULL);
while (i < info->nrOfSamples) {
if (info->benchmark->state == BENCHMARK_STATE_RUNNING ) {
//TODO race condition?? or not because of the mutex on changing the state
__sync_add_and_fetch(&info->benchmark->threadsRunning, 1);
benchmark_runSamples(info, &i, &info->benchmark->state);
__sync_sub_and_fetch(&info->benchmark->threadsRunning, 1);
} else {
usleep(WAIT_TIME);
}
}
gettimeofday(&info->end, NULL);
}
static void benchmark_runSamples(thread_info_t *info, int *i, volatile benchmark_state_t *state) {
int nrOfSamples = info->nrOfSamples;
unsigned int result = info->result;
math_service_pt math = info->benchmark->math;
for (; *i < nrOfSamples && *state == BENCHMARK_STATE_RUNNING; *i += 1) {
result = math->calc(math->handle, result, *i);
}
info->result = result;
}
char * benchmark_getName(benchmark_pt benchmark) {
return (char *)BENCHMARK_NAME;
}
static void benchmark_continue(benchmark_pt benchmark) {
benchmark->state = BENCHMARK_STATE_RUNNING;
unsigned long waitTime = 0;
while (benchmark->threadsRunning < benchmark->nrOfThreads) {
usleep(WAIT_TIME);
waitTime += WAIT_TIME;
if (waitTime > 1000 * 1000 * 2) {
printf("still waiting to stop, running threads are %i\n",
benchmark->threadsRunning);
}
}
}
static void benchmark_interrupt(benchmark_pt benchmark) {
int i = 0;
unsigned long waitTime = 0;
if (benchmark->state == BENCHMARK_STATE_RUNNING) {
benchmark->state = BENCHMARK_STATE_INTERRUPTED;
while (benchmark->threadsRunning > 0) {
usleep(WAIT_TIME);
waitTime += WAIT_TIME;
if (waitTime > 1000 * 1000 * 2) {
printf("still waiting to stop, running threads are %i\n",
benchmark->threadsRunning);
}
}
}
}
celix_status_t benchmark_addMathService(benchmark_pt benchmark, math_service_pt mathService) {
pthread_mutex_lock(&benchmark->mutex);
benchmark_interrupt(benchmark);
benchmark->math = mathService;
benchmark_continue(benchmark);
pthread_mutex_unlock(&benchmark->mutex);
return CELIX_SUCCESS;
}
celix_status_t benchmark_removeMathService(benchmark_pt benchmark, math_service_pt mathService) {
pthread_mutex_lock(&benchmark->mutex);
if (benchmark->math == mathService) {
benchmark_interrupt(benchmark);
benchmark->math = NULL;
benchmark_continue(benchmark);
}
pthread_mutex_unlock(&benchmark->mutex);
return CELIX_SUCCESS;
}
double benchmark_getSampleFactor(benchmark_pt benchmark) {
return SAMPLE_FACTOR;
}