app: split performance tests

Signed-off-by: Intel
This commit is contained in:
Intel 2012-12-20 00:00:00 +01:00 committed by Thomas Monjalon
parent e2cc79b75d
commit e242c810e9
12 changed files with 1722 additions and 1131 deletions

View File

@ -56,10 +56,13 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ring.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_rwlock.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_timer.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool_perf.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_mbuf.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_logs.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_memcpy.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_memcpy_perf.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_hash.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_hash_perf.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_lpm.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_debug.c
SRCS-$(CONFIG_RTE_APP_TEST) += test_errno.c

View File

@ -125,6 +125,8 @@ static void cmd_autotest_parsed(void *parsed_result,
ret |= test_errno();
if (all || !strcmp(res->autotest, "hash_autotest"))
ret |= test_hash();
if (all || !strcmp(res->autotest, "hash_perf_autotest"))
ret |= test_hash_perf();
if (all || !strcmp(res->autotest, "lpm_autotest"))
ret |= test_lpm();
if (all || !strcmp(res->autotest, "cpuflags_autotest"))
@ -157,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result,
ret |= test_timer();
if (all || !strcmp(res->autotest, "mempool_autotest"))
ret |= test_mempool();
if (all || !strcmp(res->autotest, "mempool_perf_autotest"))
ret |= test_mempool_perf();
if (all || !strcmp(res->autotest, "memcpy_perf_autotest"))
ret |= test_memcpy_perf();
if (ret == 0)
printf("Test OK\n");
@ -183,6 +189,8 @@ cmdline_parse_token_string_t cmd_autotest_autotest =
"alarm_autotest#interrupt_autotest#"
"version_autotest#eal_fs_autotest#"
"cmdline_autotest#"
"mempool_perf_autotest#hash_perf_autotest#"
"memcpy_perf_autotest#"
"all_autotests");
cmdline_parse_inst_t cmd_autotest = {

View File

@ -63,11 +63,14 @@ int test_logs(void);
int test_memzone(void);
int test_ring(void);
int test_mempool(void);
int test_mempool_perf(void);
int test_mbuf(void);
int test_timer(void);
int test_malloc(void);
int test_memcpy(void);
int test_memcpy_perf(void);
int test_hash(void);
int test_hash_perf(void);
int test_lpm(void);
int test_debug(void);
int test_errno(void);

File diff suppressed because it is too large Load Diff

785
app/test/test_hash_perf.c Normal file
View File

@ -0,0 +1,785 @@
/*-
* BSD LICENSE
*
* Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/queue.h>
#include <rte_common.h>
#include <rte_lcore.h>
#include <rte_malloc.h>
#include <rte_cycles.h>
#include <rte_random.h>
#include <rte_memory.h>
#include <rte_memzone.h>
#include <rte_tailq.h>
#include <rte_eal.h>
#include <rte_ip.h>
#include <rte_string_fns.h>
#include <rte_hash.h>
#include <rte_fbk_hash.h>
#include <rte_jhash.h>
#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
#include <rte_hash_crc.h>
#endif
#include <cmdline_parse.h>
#include "test.h"
#ifdef RTE_LIBRTE_HASH
/* Types of hash table performance test that can be performed */
enum hash_test_t {
ADD_ON_EMPTY, /*< Add keys to empty table */
DELETE_ON_EMPTY, /*< Attempt to delete keys from empty table */
LOOKUP_ON_EMPTY, /*< Attempt to find keys in an empty table */
ADD_UPDATE, /*< Add/update keys in a full table */
DELETE, /*< Delete keys from a full table */
LOOKUP /*< Find keys in a full table */
};
/* Function type for hash table operations. */
typedef int32_t (*hash_operation)(const struct rte_hash *h, const void *key);
/* Structure to hold parameters used to run a hash table performance test */
struct tbl_perf_test_params {
enum hash_test_t test_type;
uint32_t num_iterations;
uint32_t entries;
uint32_t bucket_entries;
uint32_t key_len;
rte_hash_function hash_func;
uint32_t hash_func_init_val;
};
#define ITERATIONS 10000
#define LOCAL_FBK_HASH_ENTRIES_MAX (1 << 15)
/*******************************************************************************
* Hash function performance test configuration section. Each performance test
* will be performed HASHTEST_ITERATIONS times.
*
* The five arrays below control what tests are performed. Every combination
* from the array entries is tested.
*/
#define HASHTEST_ITERATIONS 1000000
#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc};
#else
static rte_hash_function hashtest_funcs[] = {rte_jhash};
#endif
static uint32_t hashtest_initvals[] = {0};
static uint32_t hashtest_key_lens[] = {2, 4, 5, 6, 7, 8, 10, 11, 15, 16, 21, 31, 32, 33, 63, 64};
/******************************************************************************/
/*******************************************************************************
* Hash table performance test configuration section.
*/
struct tbl_perf_test_params tbl_perf_params[] =
{
/* Small table, add */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_ON_EMPTY, 1024, 1024, 1, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 1, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 1, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 1, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 64, rte_jhash, 0},
/* Small table, update */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_UPDATE, ITERATIONS, 1024, 1, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 1, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 1, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 1, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 64, rte_jhash, 0},
/* Small table, lookup */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ LOOKUP, ITERATIONS, 1024, 1, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 1, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 1, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 1, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 64, rte_jhash, 0},
/* Big table, add */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 16, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 32, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 48, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 64, rte_jhash, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 64, rte_jhash, 0},
/* Big table, update */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 16, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 32, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 48, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 64, rte_jhash, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 64, rte_jhash, 0},
/* Big table, lookup */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ LOOKUP, ITERATIONS, 1048576, 1, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 16, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 1, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 32, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 1, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 48, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 1, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 64, rte_jhash, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 64, rte_jhash, 0},
#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
/* Small table, add */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_ON_EMPTY, 1024, 1024, 1, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 1, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 1, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 1, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 2, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 4, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 8, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1024, 1024, 16, 64, rte_hash_crc, 0},
/* Small table, update */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_UPDATE, ITERATIONS, 1024, 1, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 1, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 1, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 1, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 2, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 4, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 8, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1024, 16, 64, rte_hash_crc, 0},
/* Small table, lookup */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ LOOKUP, ITERATIONS, 1024, 1, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 1, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 1, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 1, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 2, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 4, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 8, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1024, 16, 64, rte_hash_crc, 0},
/* Big table, add */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 16, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 32, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 48, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 1, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 2, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 4, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 8, 64, rte_hash_crc, 0},
{ ADD_ON_EMPTY, 1048576, 1048576, 16, 64, rte_hash_crc, 0},
/* Big table, update */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 16, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 32, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 48, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 1, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 2, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 4, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 8, 64, rte_hash_crc, 0},
{ ADD_UPDATE, ITERATIONS, 1048576, 16, 64, rte_hash_crc, 0},
/* Big table, lookup */
/* Test type | Iterations | Entries | BucketSize | KeyLen | HashFunc | InitVal */
{ LOOKUP, ITERATIONS, 1048576, 1, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 16, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 1, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 32, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 1, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 48, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 1, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 2, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 4, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 8, 64, rte_hash_crc, 0},
{ LOOKUP, ITERATIONS, 1048576, 16, 64, rte_hash_crc, 0},
#endif
};
/******************************************************************************/
/*
* Check condition and return an error if true. Assumes that "handle" is the
* name of the hash structure pointer to be freed.
*/
#define RETURN_IF_ERROR(cond, str, ...) do { \
if (cond) { \
printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
if (handle) rte_hash_free(handle); \
return -1; \
} \
} while(0)
#define RETURN_IF_ERROR_FBK(cond, str, ...) do { \
if (cond) { \
printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
if (handle) rte_fbk_hash_free(handle); \
return -1; \
} \
} while(0)
/*
* Find average of array of numbers.
*/
static double
get_avg(const uint32_t *array, uint32_t size)
{
double sum = 0;
unsigned i;
for (i = 0; i < size; i++)
sum += array[i];
return sum / (double)size;
}
/*
* To help print out name of hash functions.
*/
static const char *get_hash_name(rte_hash_function f)
{
if (f == rte_jhash)
return "jhash";
#ifdef RTE_MACHINE_CPUFLAG_SSE4_2
if (f == rte_hash_crc)
return "rte_hash_crc";
#endif
return "UnknownHash";
}
/*
* Do a single performance test, of one type of operation.
*
* @param h
* hash table to run test on
* @param func
* function to call (add, delete or lookup function)
* @param avg_occupancy
* The average number of entries in each bucket of the hash table
* @param invalid_pos_count
* The amount of errors (e.g. due to a full bucket).
* @return
* The average number of ticks per hash function call. A negative number
* signifies failure.
*/
static double
run_single_tbl_perf_test(const struct rte_hash *h, hash_operation func,
const struct tbl_perf_test_params *params, double *avg_occupancy,
uint32_t *invalid_pos_count)
{
uint64_t begin, end, ticks = 0;
uint8_t *key = NULL;
uint32_t *bucket_occupancies = NULL;
uint32_t num_buckets, i, j;
int32_t pos;
/* Initialise */
num_buckets = params->entries / params->bucket_entries;
key = (uint8_t *) rte_zmalloc("hash key",
params->key_len * sizeof(uint8_t), 16);
if (key == NULL)
return -1;
bucket_occupancies = (uint32_t *) rte_zmalloc("bucket occupancies",
num_buckets * sizeof(uint32_t), 16);
if (bucket_occupancies == NULL) {
rte_free(key);
return -1;
}
ticks = 0;
*invalid_pos_count = 0;
for (i = 0; i < params->num_iterations; i++) {
/* Prepare inputs for the current iteration */
for (j = 0; j < params->key_len; j++)
key[j] = (uint8_t) rte_rand();
/* Perform operation, and measure time it takes */
begin = rte_rdtsc();
pos = func(h, key);
end = rte_rdtsc();
ticks += end - begin;
/* Other work per iteration */
if (pos < 0)
*invalid_pos_count += 1;
else
bucket_occupancies[pos / params->bucket_entries]++;
}
*avg_occupancy = get_avg(bucket_occupancies, num_buckets);
rte_free(bucket_occupancies);
rte_free(key);
return (double)ticks / params->num_iterations;
}
/*
* To help print out what tests are being done.
*/
static const char *
get_tbl_perf_test_desc(enum hash_test_t type)
{
switch (type){
case ADD_ON_EMPTY: return "Add on Empty";
case DELETE_ON_EMPTY: return "Delete on Empty";
case LOOKUP_ON_EMPTY: return "Lookup on Empty";
case ADD_UPDATE: return "Add Update";
case DELETE: return "Delete";
case LOOKUP: return "Lookup";
default: return "UNKNOWN";
}
}
/*
* Run a hash table performance test based on params.
*/
static int
run_tbl_perf_test(struct tbl_perf_test_params *params)
{
static unsigned calledCount = 5;
struct rte_hash_parameters hash_params = {
.entries = params->entries,
.bucket_entries = params->bucket_entries,
.key_len = params->key_len,
.hash_func = params->hash_func,
.hash_func_init_val = params->hash_func_init_val,
.socket_id = rte_socket_id(),
};
struct rte_hash *handle;
double avg_occupancy = 0, ticks = 0;
uint32_t num_iterations, invalid_pos;
char name[RTE_HASH_NAMESIZE];
char hashname[RTE_HASH_NAMESIZE];
rte_snprintf(name, 32, "test%u", calledCount++);
hash_params.name = name;
handle = rte_hash_create(&hash_params);
RETURN_IF_ERROR(handle == NULL, "hash creation failed");
switch (params->test_type){
case ADD_ON_EMPTY:
ticks = run_single_tbl_perf_test(handle, rte_hash_add_key,
params, &avg_occupancy, &invalid_pos);
break;
case DELETE_ON_EMPTY:
ticks = run_single_tbl_perf_test(handle, rte_hash_del_key,
params, &avg_occupancy, &invalid_pos);
break;
case LOOKUP_ON_EMPTY:
ticks = run_single_tbl_perf_test(handle, rte_hash_lookup,
params, &avg_occupancy, &invalid_pos);
break;
case ADD_UPDATE:
num_iterations = params->num_iterations;
params->num_iterations = params->entries;
run_single_tbl_perf_test(handle, rte_hash_add_key, params,
&avg_occupancy, &invalid_pos);
params->num_iterations = num_iterations;
ticks = run_single_tbl_perf_test(handle, rte_hash_add_key,
params, &avg_occupancy, &invalid_pos);
break;
case DELETE:
num_iterations = params->num_iterations;
params->num_iterations = params->entries;
run_single_tbl_perf_test(handle, rte_hash_add_key, params,
&avg_occupancy, &invalid_pos);
params->num_iterations = num_iterations;
ticks = run_single_tbl_perf_test(handle, rte_hash_del_key,
params, &avg_occupancy, &invalid_pos);
break;
case LOOKUP:
num_iterations = params->num_iterations;
params->num_iterations = params->entries;
run_single_tbl_perf_test(handle, rte_hash_add_key, params,
&avg_occupancy, &invalid_pos);
params->num_iterations = num_iterations;
ticks = run_single_tbl_perf_test(handle, rte_hash_lookup,
params, &avg_occupancy, &invalid_pos);
break;
default: return -1;
}
rte_snprintf(hashname, RTE_HASH_NAMESIZE, "%s", get_hash_name(params->hash_func));
printf("%-12s, %-15s, %-16u, %-7u, %-18u, %-8u, %-19.2f, %.2f\n",
hashname,
get_tbl_perf_test_desc(params->test_type),
(unsigned) params->key_len,
(unsigned) params->entries,
(unsigned) params->bucket_entries,
(unsigned) invalid_pos,
avg_occupancy,
ticks
);
/* Free */
rte_hash_free(handle);
return 0;
}
/*
* Run all hash table performance tests.
*/
static int run_all_tbl_perf_tests(void)
{
unsigned i;
printf(" *** Hash table performance test results ***\n");
printf("Hash Func. , Operation , Key size (bytes), Entries, "
"Entries per bucket, Errors , Avg. bucket entries, Ticks/Op.\n");
/* Loop through every combination of test parameters */
for (i = 0;
i < sizeof(tbl_perf_params) / sizeof(struct tbl_perf_test_params);
i++) {
/* Perform test */
if (run_tbl_perf_test(&tbl_perf_params[i]) < 0)
return -1;
}
return 0;
}
/*
* Test a hash function.
*/
static void run_hash_func_test(rte_hash_function f, uint32_t init_val,
uint32_t key_len)
{
static uint8_t key[RTE_HASH_KEY_LENGTH_MAX];
uint64_t ticks = 0, start, end;
unsigned i, j;
for (i = 0; i < HASHTEST_ITERATIONS; i++) {
for (j = 0; j < key_len; j++)
key[j] = (uint8_t) rte_rand();
start = rte_rdtsc();
f(key, key_len, init_val);
end = rte_rdtsc();
ticks += end - start;
}
printf("%-12s, %-18u, %-13u, %.02f\n", get_hash_name(f), (unsigned) key_len,
(unsigned) init_val, (double)ticks / HASHTEST_ITERATIONS);
}
/*
* Test all hash functions.
*/
static void run_hash_func_tests(void)
{
unsigned i, j, k;
printf("\n\n *** Hash function performance test results ***\n");
printf(" Number of iterations for each test = %d\n",
HASHTEST_ITERATIONS);
printf("Hash Func. , Key Length (bytes), Initial value, Ticks/Op.\n");
for (i = 0;
i < sizeof(hashtest_funcs) / sizeof(rte_hash_function);
i++) {
for (j = 0;
j < sizeof(hashtest_initvals) / sizeof(uint32_t);
j++) {
for (k = 0;
k < sizeof(hashtest_key_lens) / sizeof(uint32_t);
k++) {
run_hash_func_test(hashtest_funcs[i],
hashtest_initvals[j],
hashtest_key_lens[k]);
}
}
}
}
/* Control operation of performance testing of fbk hash. */
#define LOAD_FACTOR 0.667 /* How full to make the hash table. */
#define TEST_SIZE 1000000 /* How many operations to time. */
#define TEST_ITERATIONS 30 /* How many measurements to take. */
#define ENTRIES (1 << 15) /* How many entries. */
static int
fbk_hash_perf_test(void)
{
struct rte_fbk_hash_params params = {
.name = "fbk_hash_test",
.entries = ENTRIES,
.entries_per_bucket = 4,
.socket_id = rte_socket_id(),
};
struct rte_fbk_hash_table *handle;
uint32_t keys[ENTRIES] = {0};
unsigned indexes[TEST_SIZE];
uint64_t lookup_time = 0;
unsigned added = 0;
unsigned value = 0;
unsigned i, j;
handle = rte_fbk_hash_create(&params);
RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation failed");
/* Generate random keys and values. */
for (i = 0; i < ENTRIES; i++) {
uint32_t key = (uint32_t)rte_rand();
key = ((uint64_t)key << 32) | (uint64_t)rte_rand();
uint16_t val = (uint16_t)rte_rand();
if (rte_fbk_hash_add_key(handle, key, val) == 0) {
keys[added] = key;
added++;
}
if (added > (LOAD_FACTOR * ENTRIES)) {
break;
}
}
for (i = 0; i < TEST_ITERATIONS; i++) {
uint64_t begin;
uint64_t end;
/* Generate random indexes into keys[] array. */
for (j = 0; j < TEST_SIZE; j++) {
indexes[j] = rte_rand() % added;
}
begin = rte_rdtsc();
/* Do lookups */
for (j = 0; j < TEST_SIZE; j++) {
value += rte_fbk_hash_lookup(handle, keys[indexes[j]]);
}
end = rte_rdtsc();
lookup_time += (double)(end - begin);
}
printf("\n\n *** FBK Hash function performance test results ***\n");
/*
* The use of the 'value' variable ensures that the hash lookup is not
* being optimised out by the compiler.
*/
if (value != 0)
printf("Number of ticks per lookup = %g\n",
(double)lookup_time /
((double)TEST_ITERATIONS * (double)TEST_SIZE));
rte_fbk_hash_free(handle);
return 0;
}
/*
* Do all unit and performance tests.
*/
int test_hash_perf(void)
{
if (run_all_tbl_perf_tests() < 0)
return -1;
run_hash_func_tests();
if (fbk_hash_perf_test() < 0)
return -1;
return 0;
}
#else
int
test_hash_perf(void)
{
printf("The Hash library is not included in this build\n");
return 0;
}
#endif

View File

@ -82,210 +82,7 @@ static size_t buf_sizes[TEST_VALUE_RANGE];
/* Data is aligned on this many bytes (power of 2) */
#define ALIGNMENT_UNIT 16
/*
* Pointers used in performance tests. The two large buffers are for uncached
* access where random addresses within the buffer are used for each
* memcpy. The two small buffers are for cached access.
*/
static uint8_t *large_buf_read, *large_buf_write,
*small_buf_read, *small_buf_write;
/* Initialise data buffers. */
static int
init_buffers(void)
{
unsigned i;
large_buf_read = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT);
if (large_buf_read == NULL)
goto error_large_buf_read;
large_buf_write = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT);
if (large_buf_write == NULL)
goto error_large_buf_write;
small_buf_read = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT);
if (small_buf_read == NULL)
goto error_small_buf_read;
small_buf_write = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT);
if (small_buf_write == NULL)
goto error_small_buf_write;
for (i = 0; i < LARGE_BUFFER_SIZE; i++)
large_buf_read[i] = rte_rand();
for (i = 0; i < SMALL_BUFFER_SIZE; i++)
small_buf_read[i] = rte_rand();
return 0;
error_small_buf_write:
rte_free(small_buf_read);
error_small_buf_read:
rte_free(large_buf_write);
error_large_buf_write:
rte_free(large_buf_read);
error_large_buf_read:
printf("ERROR: not enough memory");
return -1;
}
/* Cleanup data buffers */
static void
free_buffers(void)
{
rte_free(large_buf_read);
rte_free(large_buf_write);
rte_free(small_buf_read);
rte_free(small_buf_write);
}
/*
* Get a random offset into large array, with enough space needed to perform
* max copy size. Offset is aligned.
*/
static inline size_t
get_rand_offset(void)
{
return ((rte_rand() % (LARGE_BUFFER_SIZE - SMALL_BUFFER_SIZE)) &
~(ALIGNMENT_UNIT - 1));
}
/* Fill in source and destination addresses. */
static inline void
fill_addr_arrays(size_t *dst_addr, int is_dst_cached,
size_t *src_addr, int is_src_cached)
{
unsigned int i;
for (i = 0; i < TEST_BATCH_SIZE; i++) {
dst_addr[i] = (is_dst_cached) ? 0 : get_rand_offset();
src_addr[i] = (is_src_cached) ? 0 : get_rand_offset();
}
}
/* Integer division with round to nearest */
static inline uint64_t
div_round(uint64_t dividend, uint64_t divisor)
{
return ((2 * dividend) + divisor) / (2 * divisor);
}
/*
* WORKAROUND: For some reason the first test doing an uncached write
* takes a very long time (~25 times longer than is expected). So we do
* it once without timing.
*/
static void
do_uncached_write(uint8_t *dst, int is_dst_cached,
const uint8_t *src, int is_src_cached, size_t size)
{
unsigned i, j;
size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE];
for (i = 0; i < (TEST_ITERATIONS / TEST_BATCH_SIZE); i++) {
fill_addr_arrays(dst_addrs, is_dst_cached,
src_addrs, is_src_cached);
for (j = 0; j < TEST_BATCH_SIZE; j++)
rte_memcpy(dst+dst_addrs[j], src+src_addrs[j], size);
}
}
/*
* Run a single memcpy performance test. This is a macro to ensure that if
* the "size" parameter is a constant it won't be converted to a variable.
*/
#define SINGLE_PERF_TEST(dst, is_dst_cached, src, is_src_cached, size) do { \
unsigned int iter, t; \
size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE]; \
uint64_t start_time, total_time = 0; \
uint64_t total_time2 = 0; \
for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \
fill_addr_arrays(dst_addrs, is_dst_cached, \
src_addrs, is_src_cached); \
start_time = rte_rdtsc(); \
for (t = 0; t < TEST_BATCH_SIZE; t++) \
rte_memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \
total_time += rte_rdtsc() - start_time; \
} \
for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \
fill_addr_arrays(dst_addrs, is_dst_cached, \
src_addrs, is_src_cached); \
start_time = rte_rdtsc(); \
for (t = 0; t < TEST_BATCH_SIZE; t++) \
memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \
total_time2 += rte_rdtsc() - start_time; \
} \
printf("%9u/", (unsigned)div_round(total_time, TEST_ITERATIONS)); \
printf("%4u", (unsigned)div_round(total_time2, TEST_ITERATIONS)); \
} while (0)
/* Run memcpy() tests for each cached/uncached permutation. */
#define ALL_PERF_TESTS_FOR_SIZE(n) do { \
if (__builtin_constant_p(n)) \
printf("\nC%6u ", (unsigned)n); \
else \
printf("\n%7u ", (unsigned)n); \
SINGLE_PERF_TEST(small_buf_write, 1, small_buf_read, 1, n); \
SINGLE_PERF_TEST(large_buf_write, 0, small_buf_read, 1, n); \
SINGLE_PERF_TEST(small_buf_write, 1, large_buf_read, 0, n); \
SINGLE_PERF_TEST(large_buf_write, 0, large_buf_read, 0, n); \
} while (0)
/*
* Run performance tests for a number of different sizes and cached/uncached
* permutations.
*/
static int
perf_test(void)
{
const unsigned num_buf_sizes = sizeof(buf_sizes) / sizeof(buf_sizes[0]);
unsigned i;
int ret;
ret = init_buffers();
if (ret != 0)
return ret;
#if TEST_VALUE_RANGE != 0
/* Setup buf_sizes array, if required */
for (i = 0; i < TEST_VALUE_RANGE; i++)
buf_sizes[i] = i;
#endif
/* See function comment */
do_uncached_write(large_buf_write, 0, small_buf_read, 1, SMALL_BUFFER_SIZE);
printf("\n** rte_memcpy()/memcpy performance tests **\n"
"======= ============== ============== ============== ==============\n"
" Size Cache to cache Cache to mem Mem to cache Mem to mem\n"
"(bytes) (ticks) (ticks) (ticks) (ticks)\n"
"------- -------------- -------------- -------------- --------------");
/* Do tests where size is a variable */
for (i = 0; i < num_buf_sizes; i++) {
ALL_PERF_TESTS_FOR_SIZE((size_t)buf_sizes[i]);
}
#ifdef RTE_MEMCPY_BUILTIN_CONSTANT_P
/* Do tests where size is a compile-time constant */
ALL_PERF_TESTS_FOR_SIZE(63U);
ALL_PERF_TESTS_FOR_SIZE(64U);
ALL_PERF_TESTS_FOR_SIZE(65U);
ALL_PERF_TESTS_FOR_SIZE(255U);
ALL_PERF_TESTS_FOR_SIZE(256U);
ALL_PERF_TESTS_FOR_SIZE(257U);
ALL_PERF_TESTS_FOR_SIZE(1023U);
ALL_PERF_TESTS_FOR_SIZE(1024U);
ALL_PERF_TESTS_FOR_SIZE(1025U);
ALL_PERF_TESTS_FOR_SIZE(1518U);
#endif
printf("\n======= ============== ============== ============== ==============\n\n");
free_buffers();
return 0;
}
/* Structure with base memcpy func pointer, and number of bytes it copies */
struct base_memcpy_func {
@ -345,6 +142,7 @@ test_single_memcpy(unsigned int off_src, unsigned int off_dst, size_t size)
unsigned int i;
uint8_t dest[SMALL_BUFFER_SIZE + ALIGNMENT_UNIT];
uint8_t src[SMALL_BUFFER_SIZE + ALIGNMENT_UNIT];
void * ret;
/* Setup buffers */
for (i = 0; i < SMALL_BUFFER_SIZE + ALIGNMENT_UNIT; i++) {
@ -353,7 +151,11 @@ test_single_memcpy(unsigned int off_src, unsigned int off_dst, size_t size)
}
/* Do the copy */
rte_memcpy(dest + off_dst, src + off_src, size);
ret = rte_memcpy(dest + off_dst, src + off_src, size);
if (ret != (dest + off_dst)) {
printf("rte_memcpy() returned %p, not %p\n",
ret, dest + off_dst);
}
/* Check nothing before offset is affected */
for (i = 0; i < off_dst; i++) {
@ -419,9 +221,6 @@ test_memcpy(void)
if (ret != 0)
return -1;
ret = base_func_test();
if (ret != 0)
return -1;
ret = perf_test();
if (ret != 0)
return -1;
return 0;

292
app/test/test_memcpy_perf.c Normal file
View File

@ -0,0 +1,292 @@
/*-
* BSD LICENSE
*
* Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <rte_common.h>
#include <cmdline_parse.h>
#include <rte_cycles.h>
#include <rte_random.h>
#include <rte_malloc.h>
#include <rte_memcpy.h>
#include "test.h"
/*
* Set this to the maximum buffer size you want to test. If it is 0, then the
* values in the buf_sizes[] array below will be used.
*/
#define TEST_VALUE_RANGE 0
/* List of buffer sizes to test */
#if TEST_VALUE_RANGE == 0
static size_t buf_sizes[] = {
0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 129, 255,
256, 257, 320, 384, 511, 512, 513, 1023, 1024, 1025, 1518, 1522, 1600,
2048, 3072, 4096, 5120, 6144, 7168, 8192
};
/* MUST be as large as largest packet size above */
#define SMALL_BUFFER_SIZE 8192
#else /* TEST_VALUE_RANGE != 0 */
static size_t buf_sizes[TEST_VALUE_RANGE];
#define SMALL_BUFFER_SIZE TEST_VALUE_RANGE
#endif /* TEST_VALUE_RANGE == 0 */
/*
* Arrays of this size are used for measuring uncached memory accesses by
* picking a random location within the buffer. Make this smaller if there are
* memory allocation errors.
*/
#define LARGE_BUFFER_SIZE (100 * 1024 * 1024)
/* How many times to run timing loop for performance tests */
#define TEST_ITERATIONS 1000000
#define TEST_BATCH_SIZE 100
/* Data is aligned on this many bytes (power of 2) */
#define ALIGNMENT_UNIT 16
/*
* Pointers used in performance tests. The two large buffers are for uncached
* access where random addresses within the buffer are used for each
* memcpy. The two small buffers are for cached access.
*/
static uint8_t *large_buf_read, *large_buf_write;
static uint8_t *small_buf_read, *small_buf_write;
/* Initialise data buffers. */
static int
init_buffers(void)
{
unsigned i;
large_buf_read = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT);
if (large_buf_read == NULL)
goto error_large_buf_read;
large_buf_write = rte_malloc("memcpy", LARGE_BUFFER_SIZE, ALIGNMENT_UNIT);
if (large_buf_write == NULL)
goto error_large_buf_write;
small_buf_read = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT);
if (small_buf_read == NULL)
goto error_small_buf_read;
small_buf_write = rte_malloc("memcpy", SMALL_BUFFER_SIZE, ALIGNMENT_UNIT);
if (small_buf_write == NULL)
goto error_small_buf_write;
for (i = 0; i < LARGE_BUFFER_SIZE; i++)
large_buf_read[i] = rte_rand();
for (i = 0; i < SMALL_BUFFER_SIZE; i++)
small_buf_read[i] = rte_rand();
return 0;
error_small_buf_write:
rte_free(small_buf_read);
error_small_buf_read:
rte_free(large_buf_write);
error_large_buf_write:
rte_free(large_buf_read);
error_large_buf_read:
printf("ERROR: not enough memory\n");
return -1;
}
/* Cleanup data buffers */
static void
free_buffers(void)
{
rte_free(large_buf_read);
rte_free(large_buf_write);
rte_free(small_buf_read);
rte_free(small_buf_write);
}
/*
* Get a random offset into large array, with enough space needed to perform
* max copy size. Offset is aligned.
*/
static inline size_t
get_rand_offset(void)
{
return ((rte_rand() % (LARGE_BUFFER_SIZE - SMALL_BUFFER_SIZE)) &
~(ALIGNMENT_UNIT - 1));
}
/* Fill in source and destination addresses. */
static inline void
fill_addr_arrays(size_t *dst_addr, int is_dst_cached,
size_t *src_addr, int is_src_cached)
{
unsigned int i;
for (i = 0; i < TEST_BATCH_SIZE; i++) {
dst_addr[i] = (is_dst_cached) ? 0 : get_rand_offset();
src_addr[i] = (is_src_cached) ? 0 : get_rand_offset();
}
}
/*
* WORKAROUND: For some reason the first test doing an uncached write
* takes a very long time (~25 times longer than is expected). So we do
* it once without timing.
*/
static void
do_uncached_write(uint8_t *dst, int is_dst_cached,
const uint8_t *src, int is_src_cached, size_t size)
{
unsigned i, j;
size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE];
for (i = 0; i < (TEST_ITERATIONS / TEST_BATCH_SIZE); i++) {
fill_addr_arrays(dst_addrs, is_dst_cached,
src_addrs, is_src_cached);
for (j = 0; j < TEST_BATCH_SIZE; j++)
rte_memcpy(dst+dst_addrs[j], src+src_addrs[j], size);
}
}
/*
* Run a single memcpy performance test. This is a macro to ensure that if
* the "size" parameter is a constant it won't be converted to a variable.
*/
#define SINGLE_PERF_TEST(dst, is_dst_cached, src, is_src_cached, size) do { \
unsigned int iter, t; \
size_t dst_addrs[TEST_BATCH_SIZE], src_addrs[TEST_BATCH_SIZE]; \
uint64_t start_time, total_time = 0; \
uint64_t total_time2 = 0; \
for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \
fill_addr_arrays(dst_addrs, is_dst_cached, \
src_addrs, is_src_cached); \
start_time = rte_rdtsc(); \
for (t = 0; t < TEST_BATCH_SIZE; t++) \
rte_memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \
total_time += rte_rdtsc() - start_time; \
} \
for (iter = 0; iter < (TEST_ITERATIONS / TEST_BATCH_SIZE); iter++) { \
fill_addr_arrays(dst_addrs, is_dst_cached, \
src_addrs, is_src_cached); \
start_time = rte_rdtsc(); \
for (t = 0; t < TEST_BATCH_SIZE; t++) \
memcpy(dst+dst_addrs[t], src+src_addrs[t], size); \
total_time2 += rte_rdtsc() - start_time; \
} \
printf("%8.0f -", (double)total_time /TEST_ITERATIONS); \
printf("%5.0f", (double)total_time2 / TEST_ITERATIONS); \
} while (0)
/* Run memcpy() tests for each cached/uncached permutation. */
#define ALL_PERF_TESTS_FOR_SIZE(n) do { \
if (__builtin_constant_p(n)) \
printf("\nC%6u", (unsigned)n); \
else \
printf("\n%7u", (unsigned)n); \
SINGLE_PERF_TEST(small_buf_write, 1, small_buf_read, 1, n); \
SINGLE_PERF_TEST(large_buf_write, 0, small_buf_read, 1, n); \
SINGLE_PERF_TEST(small_buf_write, 1, large_buf_read, 0, n); \
SINGLE_PERF_TEST(large_buf_write, 0, large_buf_read, 0, n); \
} while (0)
/*
* Run performance tests for a number of different sizes and cached/uncached
* permutations.
*/
static int
perf_test(void)
{
const unsigned num_buf_sizes = sizeof(buf_sizes) / sizeof(buf_sizes[0]);
unsigned i;
int ret;
ret = init_buffers();
if (ret != 0)
return ret;
#if TEST_VALUE_RANGE != 0
/* Setup buf_sizes array, if required */
for (i = 0; i < TEST_VALUE_RANGE; i++)
buf_sizes[i] = i;
#endif
/* See function comment */
do_uncached_write(large_buf_write, 0, small_buf_read, 1, SMALL_BUFFER_SIZE);
printf("\n** rte_memcpy() - memcpy perf. tests (C = compile-time constant) **\n"
"======= ============== ============== ============== ==============\n"
" Size Cache to cache Cache to mem Mem to cache Mem to mem\n"
"(bytes) (ticks) (ticks) (ticks) (ticks)\n"
"------- -------------- -------------- -------------- --------------");
/* Do tests where size is a variable */
for (i = 0; i < num_buf_sizes; i++) {
ALL_PERF_TESTS_FOR_SIZE((size_t)buf_sizes[i]);
}
printf("\n------- -------------- -------------- -------------- --------------");
/* Do tests where size is a compile-time constant */
ALL_PERF_TESTS_FOR_SIZE(63U);
ALL_PERF_TESTS_FOR_SIZE(64U);
ALL_PERF_TESTS_FOR_SIZE(65U);
ALL_PERF_TESTS_FOR_SIZE(255U);
ALL_PERF_TESTS_FOR_SIZE(256U);
ALL_PERF_TESTS_FOR_SIZE(257U);
ALL_PERF_TESTS_FOR_SIZE(1023U);
ALL_PERF_TESTS_FOR_SIZE(1024U);
ALL_PERF_TESTS_FOR_SIZE(1025U);
ALL_PERF_TESTS_FOR_SIZE(1518U);
printf("\n======= ============== ============== ============== ==============\n\n");
free_buffers();
return 0;
}
int
test_memcpy_perf(void)
{
int ret;
ret = perf_test();
if (ret != 0)
return -1;
return 0;
}

View File

@ -67,40 +67,12 @@
* Mempool
* =======
*
* #. Basic tests: done on one core with and without cache:
* Basic tests: done on one core with and without cache:
*
* - Get one object, put one object
* - Get two objects, put two objects
* - Get all objects, test that their content is not modified and
* put them back in the pool.
*
* #. Performance tests:
*
* Each core get *n_keep* objects per bulk of *n_get_bulk*. Then,
* objects are put back in the pool per bulk of *n_put_bulk*.
*
* This sequence is done during TIME_S seconds.
*
* This test is done on the following configurations:
*
* - Cores configuration (*cores*)
*
* - One core with cache
* - Two cores with cache
* - Max. cores with cache
* - One core without cache
* - Two cores without cache
* - Max. cores without cache
*
* - Bulk size (*n_get_bulk*, *n_put_bulk*)
*
* - Bulk get from 1 to 32
* - Bulk put from 1 to 32
*
* - Number of kept objects (*n_keep*)
*
* - 32
* - 128
*/
#define N 65536
@ -114,163 +86,6 @@ static struct rte_mempool *mp_cache, *mp_nocache;
static rte_atomic32_t synchro;
/* number of objects in one bulk operation (get or put) */
static unsigned n_get_bulk;
static unsigned n_put_bulk;
/* number of objects retrived from mempool before putting them back */
static unsigned n_keep;
/* number of enqueues / dequeues */
struct mempool_test_stats {
unsigned enq_count;
} __rte_cache_aligned;
static struct mempool_test_stats stats[RTE_MAX_LCORE];
static int
per_lcore_mempool_test(__attribute__((unused)) void *arg)
{
void *obj_table[MAX_KEEP];
unsigned i, idx;
unsigned lcore_id = rte_lcore_id();
int ret;
uint64_t start_cycles, end_cycles;
uint64_t time_diff = 0, hz = rte_get_hpet_hz();
/* n_get_bulk and n_put_bulk must be divisors of n_keep */
if (((n_keep / n_get_bulk) * n_get_bulk) != n_keep)
return -1;
if (((n_keep / n_put_bulk) * n_put_bulk) != n_keep)
return -1;
stats[lcore_id].enq_count = 0;
/* wait synchro for slaves */
if (lcore_id != rte_get_master_lcore())
while (rte_atomic32_read(&synchro) == 0);
start_cycles = rte_get_hpet_cycles();
while (time_diff/hz < TIME_S) {
for (i = 0; likely(i < (N/n_keep)); i++) {
/* get n_keep objects by bulk of n_bulk */
idx = 0;
while (idx < n_keep) {
ret = rte_mempool_get_bulk(mp, &obj_table[idx],
n_get_bulk);
if (unlikely(ret < 0)) {
rte_mempool_dump(mp);
rte_ring_dump(mp->ring);
/* in this case, objects are lost... */
return -1;
}
idx += n_get_bulk;
}
/* put the objects back */
idx = 0;
while (idx < n_keep) {
rte_mempool_put_bulk(mp, &obj_table[idx],
n_put_bulk);
idx += n_put_bulk;
}
}
end_cycles = rte_get_hpet_cycles();
time_diff = end_cycles - start_cycles;
stats[lcore_id].enq_count += N;
}
return 0;
}
/* launch all the per-lcore test, and display the result */
static int
launch_cores(unsigned cores)
{
unsigned lcore_id;
unsigned rate;
int ret;
unsigned cores_save = cores;
rte_atomic32_set(&synchro, 0);
/* reset stats */
memset(stats, 0, sizeof(stats));
printf("mempool_autotest cache=%u cores=%u n_get_bulk=%u "
"n_put_bulk=%u n_keep=%u ",
(unsigned) mp->cache_size, cores, n_get_bulk, n_put_bulk, n_keep);
if (rte_mempool_count(mp) != MEMPOOL_SIZE) {
printf("mempool is not full\n");
return -1;
}
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
if (cores == 1)
break;
cores--;
rte_eal_remote_launch(per_lcore_mempool_test,
NULL, lcore_id);
}
/* start synchro and launch test on master */
rte_atomic32_set(&synchro, 1);
ret = per_lcore_mempool_test(NULL);
cores = cores_save;
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
if (cores == 1)
break;
cores--;
if (rte_eal_wait_lcore(lcore_id) < 0)
ret = -1;
}
if (ret < 0) {
printf("per-lcore test returned -1\n");
return -1;
}
rate = 0;
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++)
rate += (stats[lcore_id].enq_count / TIME_S);
printf("rate_persec=%u\n", rate);
return 0;
}
/* for a given number of core, launch all test cases */
static int
do_one_mempool_test(unsigned cores)
{
unsigned bulk_tab_get[] = { 1, 4, 32, 0 };
unsigned bulk_tab_put[] = { 1, 4, 32, 0 };
unsigned keep_tab[] = { 32, 128, 0 };
unsigned *get_bulk_ptr;
unsigned *put_bulk_ptr;
unsigned *keep_ptr;
int ret;
for (get_bulk_ptr = bulk_tab_get; *get_bulk_ptr; get_bulk_ptr++) {
for (put_bulk_ptr = bulk_tab_put; *put_bulk_ptr; put_bulk_ptr++) {
for (keep_ptr = keep_tab; *keep_ptr; keep_ptr++) {
n_get_bulk = *get_bulk_ptr;
n_put_bulk = *put_bulk_ptr;
n_keep = *keep_ptr;
ret = launch_cores(cores);
if (ret < 0)
return -1;
}
}
}
return 0;
}
/*
@ -296,32 +111,30 @@ test_mempool_basic(void)
char *obj_data;
int ret = 0;
unsigned i, j;
unsigned old_bulk_count;
/* dump the mempool status */
rte_mempool_dump(mp);
old_bulk_count = rte_mempool_get_bulk_count(mp);
rte_mempool_dump(mp);
if (rte_mempool_set_bulk_count(mp, 0) == 0)
return -1;
if (rte_mempool_get_bulk_count(mp) == 0)
return -1;
if (rte_mempool_set_bulk_count(mp, 2) < 0)
return -1;
if (rte_mempool_get_bulk_count(mp) != 2)
return -1;
rte_mempool_dump(mp);
if (rte_mempool_set_bulk_count(mp, old_bulk_count) < 0)
return -1;
if (rte_mempool_get_bulk_count(mp) != old_bulk_count)
return -1;
rte_mempool_dump(mp);
printf("get an object\n");
if (rte_mempool_get(mp, &obj) < 0)
return -1;
rte_mempool_dump(mp);
/* tests that improve coverage */
printf("get object count\n");
if (rte_mempool_count(mp) != MEMPOOL_SIZE - 1)
return -1;
printf("get private data\n");
if (rte_mempool_get_priv(mp) !=
(char*) mp + sizeof(struct rte_mempool))
return -1;
printf("get physical address of an object\n");
if (rte_mempool_virt2phy(mp, obj) !=
(phys_addr_t) (mp->phys_addr + (phys_addr_t) ((char*) obj - (char*) mp)))
return -1;
printf("put the object back\n");
rte_mempool_put(mp, obj);
rte_mempool_dump(mp);
@ -421,9 +234,10 @@ static int test_mempool_single_producer(void)
break;
rte_spinlock_lock(&scsp_spinlock);
for (i = 0; i < MAX_KEEP; i ++) {
if (NULL != scsp_obj_table[i])
if (NULL != scsp_obj_table[i]) {
obj = scsp_obj_table[i];
break;
}
}
rte_spinlock_unlock(&scsp_spinlock);
if (i >= MAX_KEEP) {
@ -664,32 +478,6 @@ test_mempool(void)
if (test_mempool_basic_ex(mp_nocache) < 0)
return -1;
/* performance test with 1, 2 and max cores */
printf("start performance test (without cache)\n");
mp = mp_nocache;
if (do_one_mempool_test(1) < 0)
return -1;
if (do_one_mempool_test(2) < 0)
return -1;
if (do_one_mempool_test(rte_lcore_count()) < 0)
return -1;
/* performance test with 1, 2 and max cores */
printf("start performance test (with cache)\n");
mp = mp_cache;
if (do_one_mempool_test(1) < 0)
return -1;
if (do_one_mempool_test(2) < 0)
return -1;
if (do_one_mempool_test(rte_lcore_count()) < 0)
return -1;
/* mempool operation test based on single producer and single comsumer */
if (test_mempool_sp_sc() < 0)
return -1;

View File

@ -0,0 +1,334 @@
/*-
* BSD LICENSE
*
* Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/queue.h>
#include <rte_common.h>
#include <rte_log.h>
#include <rte_debug.h>
#include <rte_memory.h>
#include <rte_memzone.h>
#include <rte_launch.h>
#include <rte_cycles.h>
#include <rte_tailq.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_atomic.h>
#include <rte_branch_prediction.h>
#include <rte_ring.h>
#include <rte_mempool.h>
#include <rte_spinlock.h>
#include <rte_malloc.h>
#include <cmdline_parse.h>
#include "test.h"
/*
* Mempool performance
* =======
*
* Each core get *n_keep* objects per bulk of *n_get_bulk*. Then,
* objects are put back in the pool per bulk of *n_put_bulk*.
*
* This sequence is done during TIME_S seconds.
*
* This test is done on the following configurations:
*
* - Cores configuration (*cores*)
*
* - One core with cache
* - Two cores with cache
* - Max. cores with cache
* - One core without cache
* - Two cores without cache
* - Max. cores without cache
*
* - Bulk size (*n_get_bulk*, *n_put_bulk*)
*
* - Bulk get from 1 to 32
* - Bulk put from 1 to 32
*
* - Number of kept objects (*n_keep*)
*
* - 32
* - 128
*/
#define N 65536
#define TIME_S 5
#define MEMPOOL_ELT_SIZE 2048
#define MAX_KEEP 128
#define MEMPOOL_SIZE ((RTE_MAX_LCORE*(MAX_KEEP+RTE_MEMPOOL_CACHE_MAX_SIZE))-1)
static struct rte_mempool *mp;
static struct rte_mempool *mp_cache, *mp_nocache;
static rte_atomic32_t synchro;
/* number of objects in one bulk operation (get or put) */
static unsigned n_get_bulk;
static unsigned n_put_bulk;
/* number of objects retrived from mempool before putting them back */
static unsigned n_keep;
/* number of enqueues / dequeues */
struct mempool_test_stats {
unsigned enq_count;
} __rte_cache_aligned;
static struct mempool_test_stats stats[RTE_MAX_LCORE];
/*
* save the object number in the first 4 bytes of object data. All
* other bytes are set to 0.
*/
static void
my_obj_init(struct rte_mempool *mp, __attribute__((unused)) void *arg,
void *obj, unsigned i)
{
uint32_t *objnum = obj;
memset(obj, 0, mp->elt_size);
*objnum = i;
}
static int
per_lcore_mempool_test(__attribute__((unused)) void *arg)
{
void *obj_table[MAX_KEEP];
unsigned i, idx;
unsigned lcore_id = rte_lcore_id();
int ret;
uint64_t start_cycles, end_cycles;
uint64_t time_diff = 0, hz = rte_get_hpet_hz();
/* n_get_bulk and n_put_bulk must be divisors of n_keep */
if (((n_keep / n_get_bulk) * n_get_bulk) != n_keep)
return -1;
if (((n_keep / n_put_bulk) * n_put_bulk) != n_keep)
return -1;
stats[lcore_id].enq_count = 0;
/* wait synchro for slaves */
if (lcore_id != rte_get_master_lcore())
while (rte_atomic32_read(&synchro) == 0);
start_cycles = rte_get_hpet_cycles();
while (time_diff/hz < TIME_S) {
for (i = 0; likely(i < (N/n_keep)); i++) {
/* get n_keep objects by bulk of n_bulk */
idx = 0;
while (idx < n_keep) {
ret = rte_mempool_get_bulk(mp, &obj_table[idx],
n_get_bulk);
if (unlikely(ret < 0)) {
rte_mempool_dump(mp);
rte_ring_dump(mp->ring);
/* in this case, objects are lost... */
return -1;
}
idx += n_get_bulk;
}
/* put the objects back */
idx = 0;
while (idx < n_keep) {
rte_mempool_put_bulk(mp, &obj_table[idx],
n_put_bulk);
idx += n_put_bulk;
}
}
end_cycles = rte_get_hpet_cycles();
time_diff = end_cycles - start_cycles;
stats[lcore_id].enq_count += N;
}
return 0;
}
/* launch all the per-lcore test, and display the result */
static int
launch_cores(unsigned cores)
{
unsigned lcore_id;
unsigned rate;
int ret;
unsigned cores_save = cores;
rte_atomic32_set(&synchro, 0);
/* reset stats */
memset(stats, 0, sizeof(stats));
printf("mempool_autotest cache=%u cores=%u n_get_bulk=%u "
"n_put_bulk=%u n_keep=%u ",
(unsigned) mp->cache_size, cores, n_get_bulk, n_put_bulk, n_keep);
if (rte_mempool_count(mp) != MEMPOOL_SIZE) {
printf("mempool is not full\n");
return -1;
}
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
if (cores == 1)
break;
cores--;
rte_eal_remote_launch(per_lcore_mempool_test,
NULL, lcore_id);
}
/* start synchro and launch test on master */
rte_atomic32_set(&synchro, 1);
ret = per_lcore_mempool_test(NULL);
cores = cores_save;
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
if (cores == 1)
break;
cores--;
if (rte_eal_wait_lcore(lcore_id) < 0)
ret = -1;
}
if (ret < 0) {
printf("per-lcore test returned -1\n");
return -1;
}
rate = 0;
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++)
rate += (stats[lcore_id].enq_count / TIME_S);
printf("rate_persec=%u\n", rate);
return 0;
}
/* for a given number of core, launch all test cases */
static int
do_one_mempool_test(unsigned cores)
{
unsigned bulk_tab_get[] = { 1, 4, 32, 0 };
unsigned bulk_tab_put[] = { 1, 4, 32, 0 };
unsigned keep_tab[] = { 32, 128, 0 };
unsigned *get_bulk_ptr;
unsigned *put_bulk_ptr;
unsigned *keep_ptr;
int ret;
for (get_bulk_ptr = bulk_tab_get; *get_bulk_ptr; get_bulk_ptr++) {
for (put_bulk_ptr = bulk_tab_put; *put_bulk_ptr; put_bulk_ptr++) {
for (keep_ptr = keep_tab; *keep_ptr; keep_ptr++) {
n_get_bulk = *get_bulk_ptr;
n_put_bulk = *put_bulk_ptr;
n_keep = *keep_ptr;
ret = launch_cores(cores);
if (ret < 0)
return -1;
}
}
}
return 0;
}
int
test_mempool_perf(void)
{
rte_atomic32_init(&synchro);
/* create a mempool (without cache) */
if (mp_nocache == NULL)
mp_nocache = rte_mempool_create("perf_test_nocache", MEMPOOL_SIZE,
MEMPOOL_ELT_SIZE, 0, 0,
NULL, NULL,
my_obj_init, NULL,
SOCKET_ID_ANY, 0);
if (mp_nocache == NULL)
return -1;
/* create a mempool (with cache) */
if (mp_cache == NULL)
mp_cache = rte_mempool_create("perf_test_cache", MEMPOOL_SIZE,
MEMPOOL_ELT_SIZE,
RTE_MEMPOOL_CACHE_MAX_SIZE, 0,
NULL, NULL,
my_obj_init, NULL,
SOCKET_ID_ANY, 0);
if (mp_cache == NULL)
return -1;
/* performance test with 1, 2 and max cores */
printf("start performance test (without cache)\n");
mp = mp_nocache;
if (do_one_mempool_test(1) < 0)
return -1;
if (do_one_mempool_test(2) < 0)
return -1;
if (do_one_mempool_test(rte_lcore_count()) < 0)
return -1;
/* performance test with 1, 2 and max cores */
printf("start performance test (with cache)\n");
mp = mp_cache;
if (do_one_mempool_test(1) < 0)
return -1;
if (do_one_mempool_test(2) < 0)
return -1;
if (do_one_mempool_test(rte_lcore_count()) < 0)
return -1;
rte_mempool_list_dump();
return 0;
}

View File

@ -94,8 +94,8 @@ config:
test:
$(Q)$(MAKE) -f $(RTE_SDK)/mk/rte.sdktest.mk test
.PHONY: fast_test ring_test mempool_test
fast_test ring_test mempool_test:
.PHONY: fast_test ring_test mempool_test perf_test coverage
fast_test ring_test mempool_test perf_test coverage:
$(Q)$(MAKE) -f $(RTE_SDK)/mk/rte.sdktest.mk $@
.PHONY: testall

View File

@ -49,17 +49,37 @@ DIR := $(shell basename $(RTE_OUTPUT))
#
PHONY: test fast_test
fast_test: BLACKLIST=-Ring,Mempool
coverage: BLACKLIST=-Mempool_perf,Memcpy_perf,Hash_perf
fast_test: BLACKLIST=-Ring,Mempool_perf,Memcpy_perf,Hash_perf
ring_test: WHITELIST=Ring
mempool_test: WHITELIST=Mempool
test fast_test ring_test mempool_test:
mempool_test: WHITELIST=Mempool,Mempool_perf
perf_test:WHITELIST=Mempool_perf,Memcpy_perf,Hash_perf,Ring
test fast_test ring_test mempool_test perf_test:
@mkdir -p $(AUTOTEST_DIR) ; \
cd $(AUTOTEST_DIR) ; \
if [ -f $(RTE_OUTPUT)/app/test ]; then \
python $(RTE_SDK)/app/test/autotest.py \
$(RTE_OUTPUT)/app/test \
$(DIR) $(RTE_TARGET) \
$(RTE_TARGET) \
$(BLACKLIST) $(WHITELIST); \
else \
echo "No test found, please do a 'make build' first, or specify O=" ; \
fi
# this is a special target to ease the pain of running coverage tests
# this runs all the autotests, cmdline_test script and dump_cfg
coverage:
@mkdir -p $(AUTOTEST_DIR) ; \
cd $(AUTOTEST_DIR) ; \
if [ -f $(RTE_OUTPUT)/app/test ]; then \
python $(RTE_SDK)/app/cmdline_test/cmdline_test.py \
$(RTE_OUTPUT)/app/cmdline_test; \
ulimit -S -n 100 ; \
python $(RTE_SDK)/app/test/autotest.py \
$(RTE_OUTPUT)/app/test \
$(RTE_TARGET) \
$(BLACKLIST) $(WHITELIST) ; \
$(RTE_OUTPUT)/app/dump_cfg --file-prefix=ring_perf ; \
else \
echo "No test found, please do a 'make build' first, or specify O=" ;\
fi

View File

@ -55,4 +55,4 @@ testall: $(TESTALL_TARGETS)
%_testall:
@echo ================== Test $*
$(Q)$(MAKE) test O=$*
$(Q)$(MAKE) fast_test O=$*