mirror of
https://github.com/tiagovignatti/intel-gpu-tools.git
synced 2025-06-10 09:26:10 +00:00
benchmarks: Add gem_syslatency
Instead of measuring the wakeup latency of a GEM client, we turn the tables here and ask what is the wakeup latency of a normal process competing with GEM. In particular, a realtime process that expects deterministic latency. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
parent
0aacdac56f
commit
6cd15fb930
@ -11,5 +11,7 @@ gem_exec_tracer_la_LIBADD = -ldl
|
||||
|
||||
gem_latency_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
|
||||
gem_latency_LDADD = $(LDADD) -lpthread
|
||||
gem_syslatency_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
|
||||
gem_syslatency_LDADD = $(LDADD) -lpthread -lrt
|
||||
|
||||
EXTRA_DIST=README
|
||||
|
@ -15,6 +15,7 @@ benchmarks_PROGRAMS = \
|
||||
gem_mmap \
|
||||
gem_prw \
|
||||
gem_set_domain \
|
||||
gem_syslatency \
|
||||
gem_userptr_benchmark \
|
||||
kms_vblank \
|
||||
$(NULL)
|
||||
|
301
benchmarks/gem_syslatency.c
Normal file
301
benchmarks/gem_syslatency.c
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright © 2016 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "igt.h"
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include "drm.h"
|
||||
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#define gettid() syscall(__NR_gettid)
|
||||
#define sigev_notify_thread_id _sigev_un._tid
|
||||
|
||||
static volatile int done;
|
||||
|
||||
struct gem_busyspin {
|
||||
pthread_t thread;
|
||||
int cpu;
|
||||
unsigned long count;
|
||||
};
|
||||
|
||||
struct sys_wait {
|
||||
pthread_t thread;
|
||||
int cpu;
|
||||
struct igt_mean mean;
|
||||
};
|
||||
|
||||
static void bind_cpu(pthread_t thread, int cpu)
|
||||
{
|
||||
cpu_set_t mask;
|
||||
|
||||
if (cpu == -1)
|
||||
return;
|
||||
|
||||
CPU_ZERO(&mask);
|
||||
CPU_SET(cpu, &mask);
|
||||
|
||||
pthread_setaffinity_np(thread, sizeof(mask), &mask);
|
||||
}
|
||||
|
||||
#define LOCAL_I915_EXEC_NO_RELOC (1<<11)
|
||||
#define LOCAL_I915_EXEC_HANDLE_LUT (1<<12)
|
||||
|
||||
#define LOCAL_I915_EXEC_BSD_SHIFT (13)
|
||||
#define LOCAL_I915_EXEC_BSD_MASK (3 << LOCAL_I915_EXEC_BSD_SHIFT)
|
||||
|
||||
#define ENGINE_FLAGS (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK)
|
||||
|
||||
static bool ignore_engine(int fd, unsigned engine)
|
||||
{
|
||||
if (engine == 0)
|
||||
return true;
|
||||
|
||||
if (gem_has_bsd2(fd) && engine == I915_EXEC_BSD)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void *gem_busyspin(void *arg)
|
||||
{
|
||||
const uint32_t bbe = MI_BATCH_BUFFER_END;
|
||||
struct gem_busyspin *bs = arg;
|
||||
struct drm_i915_gem_execbuffer2 execbuf;
|
||||
struct drm_i915_gem_exec_object2 obj;
|
||||
unsigned engines[16];
|
||||
unsigned nengine;
|
||||
unsigned engine;
|
||||
int fd;
|
||||
|
||||
bind_cpu(bs->thread, bs->cpu);
|
||||
|
||||
fd = drm_open_driver(DRIVER_INTEL);
|
||||
|
||||
nengine = 0;
|
||||
for_each_engine(fd, engine)
|
||||
if (!ignore_engine(fd, engine)) engines[nengine++] = engine;
|
||||
igt_require(nengine);
|
||||
|
||||
memset(&obj, 0, sizeof(obj));
|
||||
obj.handle = gem_create(fd, 4096);
|
||||
gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe));
|
||||
|
||||
memset(&execbuf, 0, sizeof(execbuf));
|
||||
execbuf.buffers_ptr = (uintptr_t)&obj;
|
||||
execbuf.buffer_count = 1;
|
||||
execbuf.flags |= LOCAL_I915_EXEC_HANDLE_LUT;
|
||||
execbuf.flags |= LOCAL_I915_EXEC_NO_RELOC;
|
||||
if (__gem_execbuf(fd, &execbuf) == 0) {
|
||||
execbuf.flags = 0;
|
||||
gem_execbuf(fd, &execbuf);
|
||||
}
|
||||
|
||||
while (!done) {
|
||||
for (int n = 0; n < nengine; n++) {
|
||||
execbuf.flags &= ~ENGINE_FLAGS;
|
||||
execbuf.flags |= engines[n];
|
||||
gem_execbuf(fd, &execbuf);
|
||||
}
|
||||
bs->count += nengine;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MSEC_PER_SEC (1000)
|
||||
#define USEC_PER_SEC (1000 * MSEC_PER_SEC)
|
||||
#define NSEC_PER_SEC (1000 * USEC_PER_SEC)
|
||||
|
||||
static double elapsed(const struct timespec *a, const struct timespec *b)
|
||||
{
|
||||
return NSEC_PER_SEC*(b->tv_sec - a->tv_sec) + (b->tv_nsec - a ->tv_nsec);
|
||||
}
|
||||
|
||||
static void *sys_wait(void *arg)
|
||||
{
|
||||
struct sys_wait *w = arg;
|
||||
struct sigevent sev;
|
||||
timer_t timer;
|
||||
sigset_t mask;
|
||||
struct timespec now;
|
||||
#define SIG SIGRTMIN
|
||||
|
||||
bind_cpu(w->thread, w->cpu);
|
||||
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIG);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
|
||||
sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
|
||||
sev.sigev_notify_thread_id = gettid();
|
||||
sev.sigev_signo = SIG;
|
||||
timer_create(CLOCK_MONOTONIC, &sev, &timer);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
while (!done) {
|
||||
struct itimerspec its;
|
||||
int sigs;
|
||||
|
||||
its.it_value = now;
|
||||
its.it_value.tv_nsec += 100 * 1000;
|
||||
its.it_value.tv_nsec += rand() % (NSEC_PER_SEC / 1000);
|
||||
if (its.it_value.tv_nsec >= NSEC_PER_SEC) {
|
||||
its.it_value.tv_nsec -= NSEC_PER_SEC;
|
||||
its.it_value.tv_sec += 1;
|
||||
}
|
||||
its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
|
||||
timer_settime(timer, TIMER_ABSTIME, &its, NULL);
|
||||
|
||||
sigwait(&mask, &sigs);
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
igt_mean_add(&w->mean, elapsed(&its.it_value, &now));
|
||||
}
|
||||
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
timer_delete(timer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rtprio(pthread_attr_t *attr, int prio)
|
||||
{
|
||||
#ifdef PTHREAD_EXPLICIT_SCHED
|
||||
struct sched_param param = { .sched_priority = 99 };
|
||||
pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
|
||||
pthread_attr_setschedpolicy(attr, SCHED_FIFO);
|
||||
pthread_attr_setschedparam(attr, ¶m);
|
||||
#endif
|
||||
}
|
||||
|
||||
static double l_estimate(igt_stats_t *stats)
|
||||
{
|
||||
if (stats->n_values > 9)
|
||||
return igt_stats_get_trimean(stats);
|
||||
else if (stats->n_values > 5)
|
||||
return igt_stats_get_median(stats);
|
||||
else
|
||||
return igt_stats_get_mean(stats);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct gem_busyspin *busy;
|
||||
struct sys_wait *wait;
|
||||
pthread_attr_t attr;
|
||||
int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
igt_stats_t cycles, mean, max;
|
||||
int time = 10;
|
||||
int field = -1;
|
||||
int n, c;
|
||||
|
||||
while ((c = getopt(argc, argv, "t:f:")) != -1) {
|
||||
switch (c) {
|
||||
case 't':
|
||||
/* How long to run the benchmark for (seconds) */
|
||||
time = atoi(optarg);
|
||||
if (time < 0)
|
||||
time = INT_MAX;
|
||||
break;
|
||||
case 'f':
|
||||
/* Select an output field */
|
||||
field = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
busy = calloc(ncpus, sizeof(*busy));
|
||||
wait = calloc(ncpus, sizeof(*wait));
|
||||
|
||||
for (n = 0; n < ncpus; n++) {
|
||||
busy[n].cpu = n;
|
||||
pthread_create(&busy[n].thread, NULL, gem_busyspin, &busy[n]);
|
||||
}
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
rtprio(&attr, 99);
|
||||
for (n = 0; n < ncpus; n++) {
|
||||
wait[n].cpu = n;
|
||||
igt_mean_init(&wait[n].mean);
|
||||
pthread_create(&wait[n].thread, &attr, sys_wait, &wait[n]);
|
||||
}
|
||||
|
||||
sleep(time);
|
||||
done = 1;
|
||||
|
||||
igt_stats_init_with_size(&cycles, ncpus);
|
||||
for (n = 0; n < ncpus; n++) {
|
||||
pthread_join(busy[n].thread, NULL);
|
||||
igt_stats_push(&cycles, busy[n].count);
|
||||
}
|
||||
|
||||
igt_stats_init_with_size(&mean, ncpus);
|
||||
igt_stats_init_with_size(&max, ncpus);
|
||||
for (n = 0; n < ncpus; n++) {
|
||||
pthread_join(wait[n].thread, NULL);
|
||||
igt_stats_push_float(&mean, wait[n].mean.mean);
|
||||
igt_stats_push_float(&max, wait[n].mean.max);
|
||||
}
|
||||
|
||||
switch (field) {
|
||||
default:
|
||||
printf("gem_syslatency: cycles=%.0f, latency mean=%.3fns max=%.0fns\n",
|
||||
igt_stats_get_mean(&cycles),
|
||||
igt_stats_get_mean(&mean),
|
||||
l_estimate(&max));
|
||||
break;
|
||||
case 0:
|
||||
printf("%.0f\n", igt_stats_get_mean(&cycles));
|
||||
break;
|
||||
case 1:
|
||||
printf("%.3f\n", igt_stats_get_mean(&mean));
|
||||
break;
|
||||
case 2:
|
||||
printf("%.0f\n", l_estimate(&max));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
/**
|
||||
* igt_stats_t:
|
||||
@ -81,20 +82,26 @@ double igt_stats_get_variance(igt_stats_t *stats);
|
||||
double igt_stats_get_std_deviation(igt_stats_t *stats);
|
||||
|
||||
struct igt_mean {
|
||||
double mean, sq;
|
||||
double mean, sq, min, max;
|
||||
unsigned long count;
|
||||
};
|
||||
|
||||
static inline void igt_mean_init(struct igt_mean *m)
|
||||
{
|
||||
memset(m, 0, sizeof(*m));
|
||||
m->max = -HUGE_VAL;
|
||||
m->min = HUGE_VAL;
|
||||
}
|
||||
|
||||
static inline void igt_mean_add(struct igt_mean *m, double v)
|
||||
{
|
||||
double delta = v - m->mean;
|
||||
m->mean += delta / m->count++;
|
||||
m->mean += delta / ++m->count;
|
||||
m->sq += delta * (v - m->mean);
|
||||
if (v < m->min)
|
||||
m->min = v;
|
||||
if (v > m->max)
|
||||
m->max = v;
|
||||
}
|
||||
|
||||
static inline double igt_mean_get(struct igt_mean *m)
|
||||
|
Loading…
x
Reference in New Issue
Block a user