Added meson build option for enabling MeasurementFramework usage.

This commit is contained in:
Sebastian Vater 2026-04-17 08:58:48 +02:00
parent 1af9ad8035
commit 74f4aa67af
6 changed files with 836 additions and 1 deletions

View File

@ -0,0 +1,117 @@
/*
* This file is part of the SPICE measurement timestamp
*
* Copyright(c) 2026 Sebastian Vater <sebastian.vater@rz.uni-freiburg.de>
*
* This file may be licensed under the terms of the
* GNU General Public License Version 2 (the ``GPL'').
*
* Software distributed under the License is distributed
* on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the GPL for the specific language
* governing rights and limitations.
*
* You should have received a copy of the GPL along with this
* program. If not, go to http://www.gnu.org/licenses/gpl.html
* or write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/**
* @file measurement.h
* @author Sebastian Vater
* @date 29 Jan 2026
* @brief SPICE measurement timestamp header.
*
* This file contains the SPICE measurement header code
* for roundtrip data points and statistic calculations.
*
* @see https://bwsyncandshare.kit.edu/apps/user_saml/saml/selectUserBackEnd?redirectUrl=/apps/files/files/5088142741?dir%3D/OpenSourceVDI%26editing%3Dfalse%26openfile%3Dtrue
*/
#ifndef SPICE_MEASUREMENT_H
#define SPICE_MEASUREMENT_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef ENABLE_SPICE_MEASUREMENT
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <time.h>
#if defined(__GNUC__) || defined(__clang__)
#define VARIADIC_FUNC(fmt_idx, arg_idx) __attribute__((format(printf, fmt_idx, arg_idx)))
#else
#define VARIADIC_FUNC(fmt_idx, arg_idx)
#endif
/**
* @brief Measurement context for measurement data points.
*
* This structure is used for measurement data point
* storage by every function which is measurement related.
*/
typedef struct spice_ms_context {
/// Pointer to measurement data.
uint64_t *values;
/// Number of measurement data points.
size_t count;
/// Capacity of measurement data points.
size_t capacity;
/// Merge window size, amount of measurement data points to calculate mean and standard deviance for.
size_t merge_window_size;
/// Last measured timestamp.
struct timespec last;
/// Measurement log file path.
char *path;
/// Measurement context has been initialized.
bool init;
} spice_ms_context_t;
int spice_ms_create(const char *spice_ms_log_file, const size_t capacity); // Creates and initializes a log buffer for timestamp recording
int spice_ms_log(const char *format, ...) VARIADIC_FUNC(1, 2); // Opens log file, appends a formatted string and closes the log file again
void spice_ms_set_merge_window_size(size_t win_size); // Sets the merge window size which determines mean and standard deviation calculation interval
int spice_ms_start_measure(void); // Retrieves the initial measurement timestamp used for delta calculation
int spice_ms_calc_delta(void); // Calculates the delta value of two measurement timestamp data points and stores the result into the log buffer
int spice_ms_flush(void); // Flushes all measurement timestamp data from memory to disk by appending to log file
double spice_ms_calc_mean(const size_t pos, const size_t size); // Calculates the mean of all measurement timestamp data
double spice_ms_calc_std_dev(const size_t pos, const size_t size); // Calculates the standard deviation of all measurement timestamp data
spice_ms_context_t *spice_ms_context_create(const char *spice_ms_log_file, const size_t capacity); // Creates and initializes a context based log buffer for timestamp recording
void spice_ms_context_destroy(spice_ms_context_t *ctx); // Deallocates a measurement context allocated by spice_ms_context_create
int spice_ms_context_log(const spice_ms_context_t *ctx, const char *format, ...) VARIADIC_FUNC(2, 3); // Opens context log file, appends a formatted string and closes the log file again
void spice_ms_context_set_merge_window_size(spice_ms_context_t *ctx, size_t win_size); // Sets the merge window size for a context which determines mean and standard deviation calculation interval
int spice_ms_context_start_measure(spice_ms_context_t *ctx); // Retrieves the context based initial measurement timestamp used for delta calculation
int spice_ms_context_calc_delta(spice_ms_context_t *ctx); // Calculates the context based delta value of two measurement timestamp data points and stores the result into the log buffer
int spice_ms_context_flush(spice_ms_context_t *ctx); // Flushes all context based measurement timestamp data from memory to disk by appending to log file
double spice_ms_context_calc_mean(const spice_ms_context_t *ctx, const size_t pos, const size_t size); // Calculates the context based mean of all measurement timestamp data
double spice_ms_context_calc_std_dev(const spice_ms_context_t *ctx, const size_t pos, const size_t size); // Calculates the standard deviation of all measurement timestamp data
#endif
#ifdef __cplusplus
}
#endif
#endif /* SPICE_MEASUREMENT_H */

View File

@ -54,6 +54,12 @@ spice_server_link_args = []
spice_server_requires = ''
spice_protocol_version='0.14.3'
if get_option('enable_spice_measurement')
spice_server_config_data.set('ENABLE_SPICE_MEASUREMENT', '1')
add_project_arguments('-DENABLE_SPICE_MEASUREMENT', language: 'c')
add_project_arguments('-DENABLE_SPICE_MEASUREMENT', language: 'cpp')
endif
#
# Spice common subproject
#

View File

@ -51,3 +51,9 @@ option('tests',
type : 'boolean',
value : true,
description : 'Build the test binaries')
# Usage of MeasurementFramework
option('enable_spice_measurement',
type : 'boolean',
value : false,
description : 'Enable SPICE measurement support using MeasurementFramework')

View File

@ -23,6 +23,11 @@
#include "image-encoders.h"
#include "image-cache.h"
#ifdef ENABLE_SPICE_MEASUREMENT
#include "measurement.h"
#endif
#include "pixmap-cache.h"
#include "display-limits.h"
#include "common-graphics-channel.h"
@ -65,8 +70,10 @@ protected:
public:
red::unique_link<DisplayChannelClientPrivate> priv;
int is_low_bandwidth;
#ifdef ENABLE_SPICE_MEASUREMENT
spice_ms_context_t *measurement_ctx;
#endif
};
#define PALETTE_CACHE_HASH_SHIFT 8

697
server/measurement.c Normal file
View File

@ -0,0 +1,697 @@
/*
* This file is part of the SPICE measurement timestamp
*
* Copyright(c) 2026 Sebastian Vater <sebastian.vater@rz.uni-freiburg.de>
*
* This file may be licensed under the terms of the
* GNU General Public License Version 2 (the ``GPL'').
*
* Software distributed under the License is distributed
* on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the GPL for the specific language
* governing rights and limitations.
*
* You should have received a copy of the GPL along with this
* program. If not, go to http://www.gnu.org/licenses/gpl.html
* or write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#define _POSIX_C_SOURCE 199309L
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "measurement.h"
/**
* @file measurement.c
* @author Sebastian Vater
* @date 29 Jan 2026
* @brief SPICE measurement timestamp implementation.
*
* This file contains the SPICE measurement implementation
* for roundtrip data points and statistic calculations.
*
* @see https://bwsyncandshare.kit.edu/apps/user_saml/saml/selectUserBackEnd?redirectUrl=/apps/files/files/5088142741?dir%3D/OpenSourceVDI%26editing%3Dfalse%26openfile%3Dtrue
*/
#ifdef ENABLE_SPICE_MEASUREMENT
static uint64_t *spice_ms_values = NULL;
static size_t spice_ms_count = 0;
static size_t spice_ms_capacity = 0;
static bool spice_ms_init = 0;
static struct timespec spice_ms_last;
static char spice_ms_path[PATH_MAX];
static size_t spice_ms_merge_window_size = 0;
/**
* @brief Converts a timespec to 64-bit integer representing timestamp in nanoseconds.
*
* This function converts a timespec as returned by
* clock_gettime to nanoseconds represented by an
* 64-bit integer.
*
* @param[in] ts Pointer to timespec to convert.
* @return Timestamp as 64-bit integer in nanoseconds.
*/
static inline uint64_t spice_ms_ts_to_ns(const struct timespec *ts)
{
return (((uint64_t) ts->tv_sec * 1000000000ULL) + (uint64_t) ts->tv_nsec);
}
/**
* @brief Handles abnormal program termination cases for flushing buffers not yet written to disk.
*
* This function just flushes all already recorded
* timestamps to disk.
*/
static void spice_ms_atexit(void)
{
spice_ms_flush();
}
/**
* @brief Creates and initializes a log buffer for timestamp recording.
*
* This function allocates memory for a fixed
* capacity and initializes the necessary
* structures for doing the measurement task.
*
* @param[in] spice_ms_log_file Pointer to log file name to write
* the measurement timestamp data to. May NOT
* be NULL, so be careful.
* @param[in] capacity Maximum number of timestamp values to
* record. May NOT be zero.
* @retval -1 An error occured during initialization.
* @retval 0 Initialization successfully completed.
*/
int spice_ms_create(const char *spice_ms_log_file, const size_t capacity)
{
if ( (spice_ms_log_file == NULL) || (capacity == 0) || (strlen( spice_ms_log_file ) >= PATH_MAX) )
return -1;
strncpy( spice_ms_path, spice_ms_log_file, sizeof(spice_ms_path) - 1 );
spice_ms_values = malloc( capacity * sizeof(uint64_t) );
if ( spice_ms_values == NULL )
return -1;
spice_ms_capacity = capacity;
spice_ms_count = 0;
if ( atexit( spice_ms_atexit ) != 0 )
return -1;
spice_ms_init = true;
return 0;
}
/**
* @brief Opens log file, appends a formatted string and closes the log file again.
*
* This function is used to store meta
* information about the measurement
* process, e.g. bitrate of a measured
* video encoding process.
*
* @param[in] format Format string to write to log file.
* @param[in] ... Values to fill in the format string with.
* @return Non-negative number on success, a
* negative error code otherwise.
*/
int spice_ms_log(const char *format, ...)
{
if ( !spice_ms_init || (format == NULL) )
return -1;
FILE *fh = fopen( spice_ms_path, "a" );
if ( fh == NULL )
return -1;
va_list args;
va_start( args, format );
const int rc = vfprintf( fh, format, args );
va_end( args );
fclose( fh );
return rc;
}
/**
* @brief Sets the merge window size which determines mean and standard deviation calculation interval.
*
* This function allows to set the merge window
* interval in order to calculate mean and standard
* deviation values.\n
* Zero is allowed, indicating only calculating
* mean and standard deviation over the total
* set.
*
* @param[in] win_size Size of merge window to be set.
*/
void spice_ms_set_merge_window_size(size_t win_size)
{
spice_ms_merge_window_size = win_size;
}
/**
* @brief Retrieves the initial measurement timestamp used for delta calculation.
*
* THis function gets the current monotonic clock
* time and stores it for using it for delta
* calculation.
*
* @retval -1 An error occured during getting the data.
* @retval 0 Current timestamp retrieved successfully.
*/
int spice_ms_start_measure(void)
{
if ( !spice_ms_init )
return -1;
clock_gettime( CLOCK_MONOTONIC, &spice_ms_last );
return 0;
}
/**
* @brief Calculates the delta value of two measurement timestamp data points and stores the result into the log buffer.
*
* This function calculates the delta value of the
* previously stored delta value and the current
* timestamp upon calling and stores the result
* into the log buffer.\n
* If the log buffer is full, a larger buffer of
* twice the current size will be allocated.
*
* @retval -1 An error occured during allocation of the
* new log buffer in case current buffer was full.
* @retval 0 Measurement timestamp delta stored
* successfully into the log buffer.
*/
int spice_ms_calc_delta(void)
{
if ( !spice_ms_init )
return -1;
struct timespec spice_ms_now;
clock_gettime( CLOCK_MONOTONIC, &spice_ms_now );
const uint64_t delta = spice_ms_ts_to_ns( &spice_ms_now ) - spice_ms_ts_to_ns( &spice_ms_last );
spice_ms_last = spice_ms_now;
if ( spice_ms_count == spice_ms_capacity ) {
size_t new_capacity = (spice_ms_capacity << 1);
uint64_t *new_buf = realloc( spice_ms_values, new_capacity * sizeof(uint64_t) );
if ( new_buf == NULL )
return -1;
spice_ms_values = new_buf;
spice_ms_capacity = new_capacity;
}
spice_ms_values[spice_ms_count++] = delta;
return 0;
}
/**
*
* @brief Flushes all measurement timestamp data from memory to disk by appending to log file.
*
* This function appends all measurement timestamp
* data currently recorded in memory to the
* specified log file at creation and frees the
* memory log buffer afterwards.
*
* @retval -1 An error occured during data flush.
* @retval 0 Everything was flushed to disk successfully.
*/
int spice_ms_flush(void)
{
if ( !spice_ms_init || (spice_ms_values == NULL) || (spice_ms_count == 0) )
return -1;
FILE *fh = fopen( spice_ms_path, "a" );
if ( fh == NULL )
return -1;
size_t merge_win_pos = 0;
size_t pos = 0;
for ( size_t i = 0; i < spice_ms_count; i++ ) {
fprintf( fh, "%" PRIu64 "\n", (uint64_t) spice_ms_values[i] );
if ( (spice_ms_merge_window_size > 0) && ((++merge_win_pos == spice_ms_merge_window_size) || ((i + 1) == spice_ms_count))) {
const double mean = spice_ms_calc_mean( pos, merge_win_pos );
const double std_dev = spice_ms_calc_std_dev( pos, merge_win_pos );
fprintf( fh, "# count=%d, mean=%f, std_dev=%f\n", (int) merge_win_pos, mean, std_dev );
pos += merge_win_pos;
merge_win_pos = 0;
}
}
const double mean = spice_ms_calc_mean( 0, spice_ms_count );
const double std_dev = spice_ms_calc_std_dev( 0, spice_ms_count );
fprintf( fh, "# count=%d, mean=%f, std_dev=%f\n", (int) spice_ms_count, mean, std_dev );
fclose( fh );
free( spice_ms_values );
spice_ms_values = NULL;
spice_ms_count = 0;
spice_ms_capacity = 0;
spice_ms_init = false;
return 0;
}
/**
* @brief Calculates the mean of all measurement timestamp data.
*
* This function calculates the mean of all
* measurement timestamp values by adding
* them together and dividing through the
* number of measurement timestamp data
* points.\n
* Optimized for speed, addition is done
* using 64-bit integers and only the
* division part is done using floating
* point arithmetic.\n
* This might cause integer overflows in
* very large measurement delta values
* and extreme long measurement phases,
* but is fine for short deltas.
*
* @param[in] pos First position of measurement timestamp data.
* @param[in] size Number of measurement timestamp data entries,
* 0 means all remaining entries starting from pos.
* @return The mean value of all measurement timestamp
* points or NaN in case of an error.
*/
double spice_ms_calc_mean(const size_t pos, const size_t size)
{
if ( !spice_ms_init || (spice_ms_count == 0) )
return NAN;
const size_t count = ((size != 0) ? size : (spice_ms_count - pos));
uint64_t sum = 0;
for ( size_t i = pos; i < (pos + count); i++ ) {
sum += spice_ms_values[i];
}
return ((double) sum / (double) count);
}
/**
* @brief Calculates the standard deviation of all measurement timestamp data.
*
* This function calculates the standard deviation
* of all measurement timestamp values by first
* calculating the mean and then squaring the
* subtracting of the individual timestamp values
* by the the mean in order to retrieve the
* variance.
*
* @param[in] pos First position of measurement timestamp data.
* @param[in] size Number of measurement timestamp data entries,
* 0 means all remaining entries starting from pos.
* @return The standard deviation value of all measurement
* timestamp points or NaN in case of an error.
*/
double spice_ms_calc_std_dev(const size_t pos, const size_t size)
{
if ( !spice_ms_init || (spice_ms_count == 0) )
return NAN;
const size_t count = ((size != 0) ? size : (spice_ms_count - pos));
const double mean = spice_ms_calc_mean( pos, size );
double square_sums = 0.0;
for ( size_t i = pos; i < (pos + count); i++ ) {
const double delta = ((double) spice_ms_values[i] - mean);
square_sums += (delta * delta);
}
return sqrt( square_sums / (double) count );
}
/**
* @brief Creates and initializes a context based log buffer for timestamp recording.
*
* This function allocates memory for a
* context, fixed capacity and initializes
* the context for doing the measurement
* task.\n
* The context based approach allows running
* multiple measurements in parallel in one
* single exxecutable or library.
*
* @param[in] spice_ms_log_file Pointer to log file name to write
* the measurement timestamp data to. May NOT
* be NULL, so be careful.
* @param[in] capacity Maximum number of timestamp values to
* record. May NOT be zero.
* @return Pointer to created and initialized
* context or NULL upon failure.
*/
spice_ms_context_t *spice_ms_context_create(const char *spice_ms_log_file, const size_t capacity)
{
const size_t len = strlen( spice_ms_log_file );
if ( (spice_ms_log_file == NULL) || (capacity == 0) || (len >= PATH_MAX) )
return NULL;
spice_ms_context_t *ctx = (spice_ms_context_t *) malloc( sizeof(struct spice_ms_context) );
if ( ctx == NULL )
return NULL;
ctx->path = malloc( (len + 1) );
if ( ctx->path == NULL ) {
free( ctx );
return NULL;
}
memcpy( ctx->path, spice_ms_log_file, (len + 1) );
ctx->values = malloc( capacity * sizeof(uint64_t) );
if ( ctx->values == NULL ) {
free( ctx->path );
free( ctx );
return NULL;
}
ctx->capacity = capacity;
ctx->merge_window_size = 0;
ctx->count = 0;
ctx->init = true;
return ctx;
}
/**
* @brief Deallocates a measurement context allocated by spice_ms_context_create.
*
* All associated resources within this
* context are deallocated, too.
*
* @param[in] ctx Pointer to measurement
* context to deallocate. If NULL is passed,
* this function does nothing.
*/
void spice_ms_context_destroy(spice_ms_context_t *ctx)
{
if ( ctx != NULL ) {
if ( ctx->values != NULL )
free( ctx->values );
if ( ctx->path != NULL )
free( ctx->path );
free( ctx );
}
}
/**
* @brief Opens context log file, appends a formatted string and closes the log file again.
*
* This function is used to store meta
* information about the measurement
* process, e.g. bitrate of a measured
* video encoding process.
*
* @param[in] ctx Pointer to context log file.
* @param[in] format Format string to write to log file.
* @param[in] ... Values to fill in the format string with.
* @return Non-negative number on success, a
* negative error code otherwise.
*/
int spice_ms_context_log(const spice_ms_context_t *ctx, const char *format, ...)
{
if ( (ctx == NULL) || !ctx->init || (format == NULL) )
return -1;
FILE *fh = fopen( ctx->path, "a" );
if ( fh == NULL )
return -1;
va_list args;
va_start( args, format );
const int rc = vfprintf( fh, format, args );
va_end( args );
fclose( fh );
return rc;
}
/**
* @brief Sets the merge window size for a context which determines mean and standard deviation calculation interval.
*
* This function allows to set the merge window
* interval in order to calculate mean and standard
* deviation values.\n
* Zero is allowed, indicating only calculating
* mean and standard deviation over the total
* set.
*
* @param[in] ctx Pointer to merge window context.
* @param[in] win_size Size of merge window to be set.
*/
void spice_ms_context_set_merge_window_size(spice_ms_context_t *ctx, size_t win_size)
{
if ( ctx == NULL )
return;
ctx->merge_window_size = win_size;
}
/**
* @brief Retrieves the context based initial measurement timestamp used for delta calculation.
*
* THis function gets the current monotonic clock
* time and stores it for using it for delta
* calculation.
*
* @param[in] ctx Pointer to context for which to measure.
*
* @retval -1 An error occured during getting the data.
* @retval 0 Current timestamp retrieved successfully.
*/
int spice_ms_context_start_measure(spice_ms_context_t *ctx)
{
if ( (ctx == NULL) || !ctx->init )
return -1;
clock_gettime( CLOCK_MONOTONIC, &ctx->last );
return 0;
}
/**
* @brief Calculates the context based delta value of two measurement timestamp data points and stores the result into the log buffer.
*
* This function calculates the delta value of the
* previously stored delta value and the current
* timestamp upon calling and stores the result
* into the log buffer.\n
* If the log buffer is full, a larger buffer of
* twice the current size will be allocated.
*
* @param[in] ctx Pointer to context for which to calculate
* the delta for.
*
* @retval -1 An error occured during allocation of the
* new log buffer in case current buffer was full.
* @retval 0 Measurement timestamp delta stored
* successfully into the log buffer.
*/
int spice_ms_context_calc_delta(spice_ms_context_t *ctx)
{
if ( (ctx == NULL) || !ctx->init )
return -1;
struct timespec spice_ms_now;
clock_gettime( CLOCK_MONOTONIC, &spice_ms_now );
const uint64_t delta = spice_ms_ts_to_ns( &spice_ms_now ) - spice_ms_ts_to_ns( &ctx->last );
ctx->last = spice_ms_now;
if ( ctx->count == ctx->capacity ) {
size_t new_capacity = (ctx->capacity << 1);
uint64_t *new_buf = realloc( ctx->values, new_capacity * sizeof(uint64_t) );
if ( new_buf == NULL )
return -1;
ctx->values = new_buf;
ctx->capacity = new_capacity;
}
ctx->values[ctx->count++] = delta;
return 0;
}
/**
*
* @brief Flushes all context based measurement timestamp data from memory to disk by appending to log file.
*
* This function appends all measurement timestamp
* data currently recorded in memory to the
* specified log file at creation and frees the
* memory log buffer afterwards.
*
* @param[in] ctx Pointer to context for which to flush
* all measurement timestamp data for.
* @retval -1 An error occured during data flush.
* @retval 0 Everything was flushed to disk successfully.
*/
int spice_ms_context_flush(spice_ms_context_t *ctx)
{
if ( (ctx == NULL) || !ctx->init || (ctx->values == NULL) || (ctx->count == 0) )
return -1;
FILE *fh = fopen( ctx->path, "a" );
if ( fh == NULL )
return -1;
size_t merge_win_pos = 0;
size_t pos = 0;
for ( size_t i = 0; i < ctx->count; i++ ) {
fprintf( fh, "%" PRIu64 "\n", (uint64_t) ctx->values[i] );
if ( (ctx->merge_window_size > 0) && ((++merge_win_pos == ctx->merge_window_size) || ((i + 1) == ctx->count))) {
const double mean = spice_ms_context_calc_mean( ctx, pos, merge_win_pos );
const double std_dev = spice_ms_context_calc_std_dev( ctx, pos, merge_win_pos );
fprintf( fh, "# count=%d, mean=%f, std_dev=%f\n", (int) merge_win_pos, mean, std_dev );
pos += merge_win_pos;
merge_win_pos = 0;
}
}
const double mean = spice_ms_context_calc_mean( ctx, 0, ctx->count );
const double std_dev = spice_ms_context_calc_std_dev( ctx, 0, ctx->count );
fprintf( fh, "# count=%d, mean=%f, std_dev=%f\n", (int) ctx->count, mean, std_dev );
fclose( fh );
free( ctx->values );
ctx->values = NULL;
ctx->count = 0;
ctx->capacity = 0;
ctx->init = false;
return 0;
}
/**
* @brief Calculates the context based mean of all measurement timestamp data.
*
* This function calculates the mean of all
* measurement timestamp values by adding
* them together and dividing through the
* number of measurement timestamp data
* points.\n
* Optimized for speed, addition is done
* using 64-bit integers and only the
* division part is done using floating
* point arithmetic.\n
* This might cause integer overflows in
* very large measurement delta values
* and extreme long measurement phases,
* but is fine for short deltas.
*
* @param[in] ctx Pointer to context for which to
* calculatze the mean for.
* @param[in] pos First position of measurement timestamp data.
* @param[in] size Number of measurement timestamp data entries,
* 0 means all remaining entries starting from pos.
* @return The mean value of all measurement timestamp
* points or NaN in case of an error.
*/
double spice_ms_context_calc_mean(const spice_ms_context_t *ctx, const size_t pos, const size_t size)
{
if ( (ctx == NULL) || !ctx->init || (ctx->count == 0) )
return NAN;
const size_t count = ((size != 0) ? size : (ctx->count - pos));
uint64_t sum = 0;
for ( size_t i = pos; i < (pos + count); i++ ) {
sum += ctx->values[i];
}
return ((double) sum / (double) count);
}
/**
* @brief Calculates the standard deviation of all measurement timestamp data.
*
* This function calculates the standard deviation
* of all measurement timestamp values by first
* calculating the mean and then squaring the
* subtracting of the individual timestamp values
* by the the mean in order to retrieve the
* variance.
*
* @param[in] pos First position of measurement timestamp data.
* @param[in] size Number of measurement timestamp data entries,
* 0 means all remaining entries starting from pos.
* @return The standard deviation value of all measurement
* timestamp points or NaN in case of an error.
*/
double spice_ms_context_calc_std_dev(const spice_ms_context_t *ctx, const size_t pos, const size_t size)
{
if ( (ctx == NULL) || !ctx->init || (ctx->count == 0) )
return NAN;
const size_t count = ((size != 0) ? size : (ctx->count - pos));
const double mean = spice_ms_context_calc_mean( ctx, pos, size );
double square_sums = 0.0;
for ( size_t i = pos; i < (pos + count); i++ ) {
const double delta = ((double) ctx->values[i] - mean);
square_sums += (delta * delta);
}
return sqrt( square_sums / (double) count );
}
#endif

View File

@ -42,6 +42,7 @@ spice_version_h = configure_file(input : 'spice-version.h.in',
# libspice-server.so
#
spice_server_headers = [
'measurement.h',
spice_version_h,
'spice-audio.h',
'spice-char.h',
@ -113,6 +114,7 @@ spice_server_sources = [
'main-channel.h',
'main-dispatcher.cpp',
'main-dispatcher.h',
'measurement.c',
'memslot.c',
'memslot.h',
'migration-protocol.h',