/* Mis-connector - user-space router and NATP for misconfigured hosts on LAN.
Glib g_datalist / g_sequence / ... performance test.
This file is part of mis-connector.
Copyright ⓒ 2021
Petr Šilhavý <petr.silhavy@yandex.com>
SPDX-License-Identifier: GPL-3.0-or-later
iMis-connector is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mis-connector is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with zrec. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
#include <stdatomic.h>
#include <error.h>
#include <glib.h>
#include <time.h>
#include <assert.h>
#include <sys/times.h>
int ct ;
struct TEST_PARAM
{
unsigned int data_size ;
unsigned int count ;
unsigned int nloop ; //
} ;
struct DATA
{
int n ;
int psize ;
unsigned key ;
char payload[0] ;
} ;
struct MAC_DATA
{
int n ;
int psize ;
GQuark key ;
unsigned char mac[6] ;
char payload[0] ;
} ;
void *
test_sleep(void *arg)
{
sleep(1);
return NULL ;
}
GData *datalist ;
guint32
gen_key(int i)
{
return 1 + i * ( 1 << 16 ) ;
}
void *
dataset_fill(void *arg)
{
struct TEST_PARAM *tp = arg ;
g_datalist_init( &datalist );
for ( int i = 0 ; i < tp->count ; ++i )
{
struct DATA *data = g_slice_alloc0 (tp->data_size);
data->n = i ;
data->key = gen_key(i) ;
data->psize = tp->data_size - sizeof(*data) ;
g_datalist_id_set_data( &datalist, data->key, data);
}
return NULL ;
}
#define ETH_ALEN 6
static GQuark
mac_key(uint8_t mac[ETH_ALEN])
{
// return ( mac[2] << 24 ) + ( mac[3] << 16 ) +
// (mac[4] << 8) + mac[5] ;
gchar mac_str[ETH_ALEN+1] ;
memcpy(mac_str, mac, ETH_ALEN);
mac_str[6] = 0 ;
return g_quark_from_string(mac_str);
}
#define ETH_ALEN 6
static void
rand_mac(uint8_t mac[ETH_ALEN])
{
for ( int i = 0 ; i < ETH_ALEN ; ++i )
mac[i] = rand() % 256 ;
}
GSList *mac_list ;
void *
dataset_mac_fill(void *arg)
{
struct TEST_PARAM *tp = arg ;
g_datalist_init( &datalist );
for ( int i = 0 ; i < tp->count ; ++i )
{
struct MAC_DATA *data = g_slice_alloc0 (tp->data_size);
data->n = i ;
rand_mac(data->mac);
// GQuark q = mac_key(data->mac);
data->key = mac_key(data->mac);
data->psize = tp->data_size - sizeof(*data) ;
g_datalist_id_set_data( &datalist, data->key, data);
mac_list = g_slist_append(mac_list, data->mac);
}
return NULL ;
}
void *
dataset_mac_get(void *arg)
{
struct TEST_PARAM *tp = arg ;
int found = 0 ;
for ( int loop = 0 ; loop < tp->nloop ; ++loop )
for ( GSList *l = mac_list ; l ; l = l->next )
{
struct DATA *data = g_datalist_id_get_data( &datalist, mac_key(l->data) );
if (data != NULL) ++found ;
}
assert( g_slist_length(mac_list) == found );
return NULL ;
}
void *
dataset_get(void *arg)
{
struct TEST_PARAM *tp = arg ;
for ( int loop = 0 ; loop < tp->nloop ; ++loop )
for ( int i = 0 ; i < tp->count ; ++i )
{
struct DATA *data = g_datalist_id_get_data( &datalist, gen_key(i) );
if (data->n) ;
assert(data->n == i);
}
return NULL ;
}
void *
test_busy(void *arg)
{
unsigned int i = 0 , max = 1 << 31 ;
while(i < max ) ++i ;
return NULL ;
}
void
run(void * (*fce)(void *), void *arg , char *text)
{
struct tms start, end ;
struct TEST_PARAM *tp = arg ;
clock_t cl = times(&start);
if ( cl == (clock_t)-1 ) error(1, errno, "times failed" );
fce(arg);
cl = times(&end);
if ( cl == (clock_t)-1 ) error(1, errno, "times failed" );
#if 0
printf("[%s]\tUser:\tdiff %ld [%ld-%ld]\n", text,
end.tms_utime - start.tms_utime,
start.tms_utime, end.tms_utime);
#endif // 0
clock_t udiff = end.tms_utime - start.tms_utime ;
clock_t sdiff = end.tms_stime - start.tms_stime ;
printf("[%s/%dx%db]\tUser:\t%.3gs\n" , text,
tp ? tp->count : 0 ,
tp ? tp->data_size : 0 ,
( end.tms_utime - start.tms_utime ) / (double) ct );
printf("[%s/%dx%db]\tSystem:\t%.3gs\n" , text,
tp ? tp->count : 0,
tp ? tp->data_size : 0,
( end.tms_stime - start.tms_stime ) / (double) ct );
double sec = ( ( end.tms_utime - start.tms_utime ) +
( end.tms_stime - start.tms_stime ) / (double) ct )
/ (double) ct ;
long long ni = tp->count * tp->nloop ;
if ( tp && ( udiff || sdiff ) )
printf("[%s/%dx%db]\tPerf:\t%.3g/s [%lld]\n" , text,
tp->count ,
tp->data_size ,
ni / sec , ni);
}
int
main()
{
// memory_order_acq_rel
// memory_order_seq_cst
// int ncpu = g_get_num_processors();
struct TEST_PARAM tpa = { .count = 8 , .data_size = 40, .nloop = 1 << 16 } ;
struct TEST_PARAM tpb = { .count = 128 , .data_size = 40, .nloop = 1 << 16 } ;
struct TEST_PARAM tpc = { .count = 1024 , .data_size = 40, .nloop = 1 << 16 } ;
ct = sysconf (_SC_CLK_TCK);
// struct timespec ts = { .tv_sec = 0 , .tv_nsec = 42 } ;
// __atomic_clear (guard, memory_order_seq_cst);
printf("Ticks: %d\n", ct);
// run(test_sleep, NULL, "sleep test");
// run(test_busy, NULL, "busy test");
run( dataset_fill, &tpa, "dataset_fill x 8" );
run( dataset_get, &tpa, "dataset_get x 8" );
run( dataset_fill, &tpb, "dataset_fill x 128" );
run( dataset_get, &tpb, "dataset_get x 128" );
run( dataset_fill, &tpc, "dataset_fill x 1024" );
run( dataset_get, &tpc, "dataset_get x 1024" );
/////////////////////////////////////////////////////
run( dataset_mac_fill, &tpa, "dataset_mac_fill x 8" );
run( dataset_mac_get, &tpa, "dataset_mac_get x 8" );
run( dataset_mac_fill, &tpb, "dataset_mac_fill x 128" );
run( dataset_mac_get, &tpb, "dataset_mac_get x 128" );
run( dataset_mac_fill, &tpc, "dataset_mac_fill x 1024" );
run( dataset_mac_get, &tpc, "dataset_mac_get x 1024" );
return 0 ;
}