app: split performance tests
Signed-off-by: Intel
This commit is contained in:
parent
e2cc79b75d
commit
e242c810e9
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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
785
app/test/test_hash_perf.c
Normal 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(¶ms);
|
||||
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
|
@ -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
292
app/test/test_memcpy_perf.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
334
app/test/test_mempool_perf.c
Normal file
334
app/test/test_mempool_perf.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
@ -55,4 +55,4 @@ testall: $(TESTALL_TARGETS)
|
||||
|
||||
%_testall:
|
||||
@echo ================== Test $*
|
||||
$(Q)$(MAKE) test O=$*
|
||||
$(Q)$(MAKE) fast_test O=$*
|
||||
|
Loading…
Reference in New Issue
Block a user