902ae591b1
In the previous ramspeed test process, it was necessary to keep adding size to achieve speed tests for different sizes of memcpy and memset. After the modification, the results will be automatically looped from 32k to the input size.
508 lines
13 KiB
C
508 lines
13 KiB
C
/****************************************************************************
|
|
* apps/system/ramspeed/ramspeed_main.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <nuttx/irq.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <inttypes.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define RAMSPEED_PREFIX "RAM Speed: "
|
|
|
|
#if defined(UINTPTR_MAX) && UINTPTR_MAX > 0xFFFFFFFF
|
|
# define MEM_UNIT uint64_t
|
|
# define ALIGN_MASK 0x7
|
|
#else
|
|
# define MEM_UNIT uint32_t
|
|
# define ALIGN_MASK 0x3
|
|
#endif
|
|
|
|
#define COPY32 *d32 = *s32; d32++; s32++;
|
|
#define COPY8 *d8 = *s8; d8++; s8++;
|
|
#define SET32(x) *d32 = x; d32++;
|
|
#define SET8(x) *d8 = x; d8++;
|
|
#define REPEAT8(expr) expr expr expr expr expr expr expr expr
|
|
|
|
#define OPTARG_TO_VALUE(value, type, base) \
|
|
do \
|
|
{ \
|
|
FAR char *ptr; \
|
|
value = (type)strtoul(optarg, &ptr, base); \
|
|
if (*ptr != '\0') \
|
|
{ \
|
|
printf(RAMSPEED_PREFIX "Parameter error: -%c %s\n", ch, optarg); \
|
|
show_usage(argv[0], EXIT_FAILURE); \
|
|
} \
|
|
} while (0)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct ramspeed_s
|
|
{
|
|
FAR void *dest;
|
|
FAR const void *src;
|
|
size_t size;
|
|
uint8_t value;
|
|
uint32_t repeat_num;
|
|
bool irq_disable;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: show_usage
|
|
****************************************************************************/
|
|
|
|
static void show_usage(FAR const char *progname, int exitcode)
|
|
{
|
|
printf("\nUsage: %s -r <hex-address> -w <hex-address> -s <decimal-size>"
|
|
" -v <hex-value>[0x00] -n <decimal-repeat number>[100] -i\n",
|
|
progname);
|
|
printf("\nWhere:\n");
|
|
printf(" -r <hex-address> read address.\n");
|
|
printf(" -w <hex-address> write address.\n");
|
|
printf(" -s <decimal-size> number of memory locations (in bytes).\n");
|
|
printf(" -v <hex-value> value to fill in memory"
|
|
" [default value: 0x00].\n");
|
|
printf(" -n <decimal-repeat num> number of repetitions"
|
|
" [default value: 100].\n");
|
|
printf(" -i turn off interrupts while testing"
|
|
" [default value: false].\n");
|
|
exit(exitcode);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: parse_commandline
|
|
****************************************************************************/
|
|
|
|
static void parse_commandline(int argc, FAR char **argv,
|
|
FAR struct ramspeed_s *info)
|
|
{
|
|
int ch;
|
|
|
|
memset(info, 0, sizeof(struct ramspeed_s));
|
|
info->repeat_num = 100;
|
|
|
|
if (argc < 7)
|
|
{
|
|
printf(RAMSPEED_PREFIX "Missing required arguments\n");
|
|
show_usage(argv[0], EXIT_FAILURE);
|
|
}
|
|
|
|
while ((ch = getopt(argc, argv, "r:w:s:v:n:i")) != ERROR)
|
|
{
|
|
switch (ch)
|
|
{
|
|
case 'r':
|
|
OPTARG_TO_VALUE(info->src, const void *, 16);
|
|
break;
|
|
case 'w':
|
|
OPTARG_TO_VALUE(info->dest, void *, 16);
|
|
break;
|
|
case 's':
|
|
OPTARG_TO_VALUE(info->size, size_t, 10);
|
|
if (info->size < 32)
|
|
{
|
|
printf(RAMSPEED_PREFIX "<size> must >= 32");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'v':
|
|
OPTARG_TO_VALUE(info->value, uint8_t, 16);
|
|
break;
|
|
case 'n':
|
|
OPTARG_TO_VALUE(info->repeat_num, uint32_t, 10);
|
|
if (info->repeat_num == 0)
|
|
{
|
|
printf(RAMSPEED_PREFIX "<repeat number> must > 0\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
case 'i':
|
|
info->irq_disable = true;
|
|
break;
|
|
case '?':
|
|
printf(RAMSPEED_PREFIX "Unknown option: %c\n", (char)optopt);
|
|
show_usage(argv[0], EXIT_FAILURE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (info->dest == NULL || info->src == NULL || info->size == 0)
|
|
{
|
|
printf(RAMSPEED_PREFIX "Missing required arguments\n");
|
|
show_usage(argv[0], EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: get_timestamp
|
|
****************************************************************************/
|
|
|
|
static uint32_t get_timestamp(void)
|
|
{
|
|
struct timespec ts;
|
|
uint32_t ms;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
|
return ms;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: get_time_elaps
|
|
****************************************************************************/
|
|
|
|
static uint32_t get_time_elaps(uint32_t prev_time)
|
|
{
|
|
uint32_t act_time = get_timestamp();
|
|
|
|
/* If there is no overflow in sys_time simple subtract */
|
|
|
|
if (act_time >= prev_time)
|
|
{
|
|
prev_time = act_time - prev_time;
|
|
}
|
|
else
|
|
{
|
|
prev_time = UINT32_MAX - prev_time + 1;
|
|
prev_time += act_time;
|
|
}
|
|
|
|
return prev_time;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: internal_memcpy
|
|
****************************************************************************/
|
|
|
|
static void *internal_memcpy(FAR void *dst, FAR const void *src, size_t len)
|
|
{
|
|
FAR uint8_t *d8 = dst;
|
|
FAR const uint8_t *s8 = src;
|
|
|
|
uintptr_t d_align = (uintptr_t)d8 & ALIGN_MASK;
|
|
uintptr_t s_align = (uintptr_t)s8 & ALIGN_MASK;
|
|
FAR uint32_t *d32;
|
|
FAR const uint32_t *s32;
|
|
|
|
/* Byte copy for unaligned memories */
|
|
|
|
if (s_align != d_align)
|
|
{
|
|
while (len > 32)
|
|
{
|
|
REPEAT8(COPY8);
|
|
REPEAT8(COPY8);
|
|
REPEAT8(COPY8);
|
|
REPEAT8(COPY8);
|
|
len -= 32;
|
|
}
|
|
|
|
while (len)
|
|
{
|
|
COPY8;
|
|
len--;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
/* Make the memories aligned */
|
|
|
|
if (d_align)
|
|
{
|
|
d_align = ALIGN_MASK + 1 - d_align;
|
|
while (d_align && len)
|
|
{
|
|
COPY8;
|
|
d_align--;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
d32 = (FAR uint32_t *)d8;
|
|
s32 = (FAR uint32_t *)s8;
|
|
while (len > 32)
|
|
{
|
|
REPEAT8(COPY32);
|
|
len -= 32;
|
|
}
|
|
|
|
while (len > 4)
|
|
{
|
|
COPY32;
|
|
len -= 4;
|
|
}
|
|
|
|
d8 = (FAR uint8_t *)d32;
|
|
s8 = (FAR const uint8_t *)s32;
|
|
while (len)
|
|
{
|
|
COPY8;
|
|
len--;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: internal_memset
|
|
****************************************************************************/
|
|
|
|
static void internal_memset(FAR void *dst, uint8_t v, size_t len)
|
|
{
|
|
FAR uint8_t *d8 = (FAR uint8_t *)dst;
|
|
uintptr_t d_align = (uintptr_t) d8 & ALIGN_MASK;
|
|
FAR uint32_t v32;
|
|
FAR uint32_t *d32;
|
|
|
|
/* Make the address aligned */
|
|
|
|
if (d_align)
|
|
{
|
|
d_align = ALIGN_MASK + 1 - d_align;
|
|
while (d_align && len)
|
|
{
|
|
SET8(v);
|
|
len--;
|
|
d_align--;
|
|
}
|
|
}
|
|
|
|
v32 = (uint32_t)v + ((uint32_t)v << 8)
|
|
+ ((uint32_t)v << 16) + ((uint32_t)v << 24);
|
|
|
|
d32 = (FAR uint32_t *)d8;
|
|
|
|
while (len > 32)
|
|
{
|
|
REPEAT8(SET32(v32));
|
|
len -= 32;
|
|
}
|
|
|
|
while (len > 4)
|
|
{
|
|
SET32(v32);
|
|
len -= 4;
|
|
}
|
|
|
|
d8 = (FAR uint8_t *)d32;
|
|
while (len)
|
|
{
|
|
SET8(v);
|
|
len--;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: print_rate
|
|
****************************************************************************/
|
|
|
|
static void print_rate(FAR const char *name, size_t bytes,
|
|
uint32_t cost_time)
|
|
{
|
|
uint32_t rate;
|
|
if (cost_time == 0)
|
|
{
|
|
printf(RAMSPEED_PREFIX
|
|
"Time-consuming is too short,"
|
|
" please increase the <repeat number>\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
rate = (uint64_t)bytes * 1000 / cost_time / 1024;
|
|
printf(RAMSPEED_PREFIX
|
|
"%s Rate = %" PRIu32 " KB/s\t[cost: %" PRIu32 "ms]\n",
|
|
name, rate, cost_time);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: memcpy_speed_test
|
|
****************************************************************************/
|
|
|
|
static void memcpy_speed_test(FAR void *dest, FAR const void *src,
|
|
size_t size, uint32_t repeat_cnt,
|
|
bool irq_disable)
|
|
{
|
|
uint32_t start_time;
|
|
uint32_t cost_time_system;
|
|
uint32_t cost_time_internal;
|
|
uint32_t cnt;
|
|
uint32_t step;
|
|
size_t total_size;
|
|
irqstate_t flags;
|
|
|
|
printf("______memcpy performance______\n");
|
|
|
|
for (step = 32; step <= size; step <<= 1)
|
|
{
|
|
total_size = step * repeat_cnt;
|
|
|
|
if (step < 1024)
|
|
{
|
|
printf("______do %" PRIu32 " B operation______\n", step);
|
|
}
|
|
else
|
|
{
|
|
printf("______do %" PRIu32 " KB operation______\n", step / 1024);
|
|
}
|
|
|
|
if (irq_disable)
|
|
{
|
|
flags = enter_critical_section();
|
|
}
|
|
|
|
start_time = get_timestamp();
|
|
|
|
for (cnt = 0; cnt < repeat_cnt; cnt++)
|
|
{
|
|
memcpy(dest, src, step);
|
|
}
|
|
|
|
cost_time_system = get_time_elaps(start_time);
|
|
|
|
start_time = get_timestamp();
|
|
|
|
for (cnt = 0; cnt < repeat_cnt; cnt++)
|
|
{
|
|
internal_memcpy(dest, src, step);
|
|
}
|
|
|
|
cost_time_internal = get_time_elaps(start_time);
|
|
|
|
if (irq_disable)
|
|
{
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
print_rate("system memcpy():\t", total_size, cost_time_system);
|
|
print_rate("internal memcpy():\t", total_size, cost_time_internal);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: memset_speed_test
|
|
****************************************************************************/
|
|
|
|
static void memset_speed_test(FAR void *dest, uint8_t value,
|
|
size_t size, uint32_t repeat_num,
|
|
bool irq_disable)
|
|
{
|
|
uint32_t start_time;
|
|
uint32_t cost_time_system;
|
|
uint32_t cost_time_internal;
|
|
uint32_t cnt;
|
|
uint32_t step;
|
|
size_t total_size;
|
|
irqstate_t flags;
|
|
|
|
printf("______memset performance______\n");
|
|
|
|
for (step = 32; step <= size; step <<= 1)
|
|
{
|
|
total_size = step * repeat_num;
|
|
|
|
if (step < 1024)
|
|
{
|
|
printf("______do %" PRIu32 " B operation______\n", step);
|
|
}
|
|
else
|
|
{
|
|
printf("______do %" PRIu32 " KB operation______\n", step / 1024);
|
|
}
|
|
|
|
if (irq_disable)
|
|
{
|
|
flags = enter_critical_section();
|
|
}
|
|
|
|
start_time = get_timestamp();
|
|
|
|
for (cnt = 0; cnt < repeat_num; cnt++)
|
|
{
|
|
memset(dest, value, step);
|
|
}
|
|
|
|
cost_time_system = get_time_elaps(start_time);
|
|
|
|
start_time = get_timestamp();
|
|
|
|
for (cnt = 0; cnt < repeat_num; cnt++)
|
|
{
|
|
internal_memset(dest, value, step);
|
|
}
|
|
|
|
cost_time_internal = get_time_elaps(start_time);
|
|
|
|
if (irq_disable)
|
|
{
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
print_rate("system memset():\t", total_size, cost_time_system);
|
|
print_rate("internal memset():\t", total_size, cost_time_internal);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ramspeed_main
|
|
****************************************************************************/
|
|
|
|
int main(int argc, FAR char *argv[])
|
|
{
|
|
struct ramspeed_s ramspeed;
|
|
|
|
parse_commandline(argc, argv, &ramspeed);
|
|
|
|
memcpy_speed_test(ramspeed.dest, ramspeed.src,
|
|
ramspeed.size, ramspeed.repeat_num,
|
|
ramspeed.irq_disable);
|
|
|
|
memset_speed_test(ramspeed.dest, ramspeed.value,
|
|
ramspeed.size, ramspeed.repeat_num,
|
|
ramspeed.irq_disable);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|