Files
sysbench/sysbench/tests/memory/sb_memory.c
Alexey Kopytov c33ec00456 Rewrote intermediate statistics feature contributed by Mikael Ronstrom.
Removed "batch mode", --report-interval should now be used instead.
Fixed compile warnings produced by gcc with -Wextra.
2010-11-01 11:35:41 +03:00

436 lines
10 KiB
C

/* Copyright (C) 2004 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef _WIN32
#include "sb_win.h"
#endif
#include "sysbench.h"
#ifdef HAVE_SYS_IPC_H
# include <sys/ipc.h>
#endif
#ifdef HAVE_SYS_SHM_H
# include <sys/shm.h>
#endif
#define LARGE_PAGE_SIZE (4UL * 1024 * 1024)
/* Memory test arguments */
static sb_arg_t memory_args[] =
{
{"memory-block-size", "size of memory block for test",
SB_ARG_TYPE_SIZE, "1K"},
{"memory-total-size", "total size of data to transfer",
SB_ARG_TYPE_SIZE, "100G"},
{"memory-scope", "memory access scope {global,local}", SB_ARG_TYPE_STRING,
"global"},
#ifdef HAVE_LARGE_PAGES
{"memory-hugetlb", "allocate memory from HugeTLB pool", SB_ARG_TYPE_FLAG, "off"},
#endif
{"memory-oper", "type of memory operations {read, write, none}",
SB_ARG_TYPE_STRING, "write"},
{"memory-access-mode", "memory access mode {seq,rnd}", SB_ARG_TYPE_STRING, "seq"},
{NULL, NULL, SB_ARG_TYPE_NULL, NULL}
};
/* Memory test operations */
static int memory_init(void);
static void memory_print_mode(void);
static sb_request_t memory_get_request(int);
static int memory_execute_request(sb_request_t *, int);
static void memory_print_stats(sb_stat_t type);
static sb_test_t memory_test =
{
"memory",
"Memory functions speed test",
{
memory_init,
NULL,
NULL,
memory_print_mode,
memory_get_request,
memory_execute_request,
memory_print_stats,
NULL,
NULL,
NULL
},
{
NULL,
NULL,
NULL,
NULL
},
memory_args,
{NULL, NULL}
};
/* Test arguments */
static ssize_t memory_block_size;
static off_t memory_total_size;
static unsigned int memory_scope;
static unsigned int memory_oper;
static unsigned int memory_access_rnd;
#ifdef HAVE_LARGE_PAGES
static unsigned int memory_hugetlb;
#endif
/* Statistics */
static unsigned int total_ops;
static off_t total_bytes;
static off_t last_bytes;
/* Array of per-thread buffers */
static int **buffers;
/* Global buffer */
static int *buffer;
#ifdef HAVE_LARGE_PAGES
static void * hugetlb_alloc(size_t size);
#endif
int register_test_memory(sb_list_t *tests)
{
SB_LIST_ADD_TAIL(&memory_test.listitem, tests);
return 0;
}
int memory_init(void)
{
unsigned int i;
char *s;
memory_block_size = sb_get_value_size("memory-block-size");
if (memory_block_size % sizeof(int) != 0)
{
log_text(LOG_FATAL, "memory-block-size must be a multiple of %ld!", (long)sizeof(int));
return 1;
}
memory_total_size = sb_get_value_size("memory-total-size");
s = sb_get_value_string("memory-scope");
if (!strcmp(s, "global"))
memory_scope = SB_MEM_SCOPE_GLOBAL;
else if (!strcmp(s, "local"))
memory_scope = SB_MEM_SCOPE_LOCAL;
else
{
log_text(LOG_FATAL, "Invalid value for memory-scope: %s", s);
return 1;
}
#ifdef HAVE_LARGE_PAGES
memory_hugetlb = sb_get_value_flag("memory-hugetlb");
#endif
s = sb_get_value_string("memory-oper");
if (!strcmp(s, "write"))
memory_oper = SB_MEM_OP_WRITE;
else if (!strcmp(s, "read"))
memory_oper = SB_MEM_OP_READ;
else if (!strcmp(s, "none"))
memory_oper = SB_MEM_OP_NONE;
else
{
log_text(LOG_FATAL, "Invalid value for memory-oper: %s", s);
return 1;
}
s = sb_get_value_string("memory-access-mode");
if (!strcmp(s, "seq"))
memory_access_rnd = 0;
else if (!strcmp(s, "rnd"))
memory_access_rnd = 1;
else
{
log_text(LOG_FATAL, "Invalid value for memory-access-mode: %s", s);
return 1;
}
if (memory_scope == SB_MEM_SCOPE_GLOBAL)
{
#ifdef HAVE_LARGE_PAGES
if (memory_hugetlb)
buffer = (int *)hugetlb_alloc(memory_block_size);
else
#endif
buffer = (int *)malloc(memory_block_size);
if (buffer == NULL)
{
log_text(LOG_FATAL, "Failed to allocate buffer!");
return 1;
}
memset(buffer, 0, memory_block_size);
}
else
{
buffers = (int **)malloc(sb_globals.num_threads * sizeof(char *));
if (buffers == NULL)
{
log_text(LOG_FATAL, "Failed to allocate buffers array!");
return 1;
}
for (i = 0; i < sb_globals.num_threads; i++)
{
#ifdef HAVE_LARGE_PAGES
if (memory_hugetlb)
buffers[i] = (int *)hugetlb_alloc(memory_block_size);
else
#endif
buffers[i] = (int *)malloc(memory_block_size);
if (buffers[i] == NULL)
{
log_text(LOG_FATAL, "Failed to allocate buffer for thread #%d!", i);
return 1;
}
memset(buffers[i], 0, memory_block_size);
}
}
return 0;
}
sb_request_t memory_get_request(int tid)
{
sb_request_t req;
sb_mem_request_t *mem_req = &req.u.mem_request;
(void)tid; /* unused */
SB_THREAD_MUTEX_LOCK();
if (total_bytes >= memory_total_size)
{
req.type = SB_REQ_TYPE_NULL;
SB_THREAD_MUTEX_UNLOCK();
return req;
}
total_ops++;
total_bytes += memory_block_size;
SB_THREAD_MUTEX_UNLOCK();
req.type = SB_REQ_TYPE_MEMORY;
mem_req->block_size = memory_block_size;
mem_req->scope = memory_scope;
mem_req->type = memory_oper;
return req;
}
int memory_execute_request(sb_request_t *sb_req, int thread_id)
{
sb_mem_request_t *mem_req = &sb_req->u.mem_request;
int tmp = 0;
int idx;
int *buf, *end;
log_msg_t msg;
log_msg_oper_t op_msg;
long i;
unsigned int rand;
/* Prepare log message */
msg.type = LOG_MSG_TYPE_OPER;
msg.data = &op_msg;
if (mem_req->scope == SB_MEM_SCOPE_GLOBAL)
buf = buffer;
else
buf = buffers[thread_id];
end = (int *)((char *)buf + memory_block_size);
LOG_EVENT_START(msg, thread_id);
if (memory_access_rnd)
{
rand = sb_rnd();
switch (mem_req->type) {
case SB_MEM_OP_WRITE:
for (i = 0; i < memory_block_size; i++)
{
idx = (int)((double)rand / (double)SB_MAX_RND *
(double)(memory_block_size / sizeof(int)));
buf[idx] = tmp;
}
break;
case SB_MEM_OP_READ:
for (i = 0; i < memory_block_size; i++)
{
idx = (int)((double)rand / (double)SB_MAX_RND *
(double)(memory_block_size / sizeof(int)));
tmp = buf[idx];
}
break;
default:
log_text(LOG_FATAL, "Unknown memory request type:%d. Aborting...\n",
mem_req->type);
return 1;
}
}
else
{
switch (mem_req->type) {
case SB_MEM_OP_NONE:
for (; buf < end; buf++)
tmp = end - buf;
break;
case SB_MEM_OP_WRITE:
for (; buf < end; buf++)
*buf = tmp;
break;
case SB_MEM_OP_READ:
for (; buf < end; buf++)
tmp = *buf;
break;
default:
log_text(LOG_FATAL, "Unknown memory request type:%d. Aborting...\n",
mem_req->type);
return 1;
}
}
LOG_EVENT_STOP(msg, thread_id);
return 0;
}
void memory_print_mode(void)
{
char *str;
log_text(LOG_INFO, "Doing memory operations speed test");
log_text(LOG_INFO, "Memory block size: %ldK\n",
(long)(memory_block_size / 1024));
log_text(LOG_INFO, "Memory transfer size: %ldM\n",
(long)(memory_total_size / 1024 / 1024));
switch (memory_oper) {
case SB_MEM_OP_READ:
str = "read";
break;
case SB_MEM_OP_WRITE:
str = "write";
break;
case SB_MEM_OP_NONE:
str = "none";
break;
default:
str = "(unknown)";
break;
}
log_text(LOG_INFO, "Memory operations type: %s", str);
switch (memory_scope) {
case SB_MEM_SCOPE_GLOBAL:
str = "global";
break;
case SB_MEM_SCOPE_LOCAL:
str = "local";
break;
default:
str = "(unknown)";
break;
}
log_text(LOG_INFO, "Memory scope type: %s", str);
}
void memory_print_stats(sb_stat_t type)
{
double seconds;
const double megabyte = 1024.0 * 1024.0;
switch (type) {
case SB_STAT_INTERMEDIATE:
SB_THREAD_MUTEX_LOCK();
seconds = NS2SEC(sb_timer_split(&sb_globals.exec_timer));
log_timestamp(LOG_NOTICE, &sb_globals.exec_timer,
"%4.2f MB/sec,",
(double)(total_bytes - last_bytes) / megabyte / seconds);
last_bytes = total_bytes;
SB_THREAD_MUTEX_UNLOCK();
break;
case SB_STAT_CUMULATIVE:
seconds = NS2SEC(sb_timer_value(&sb_globals.exec_timer));
log_text(LOG_NOTICE, "Operations performed: %d (%8.2f ops/sec)\n",
total_ops, total_ops / seconds);
if (memory_oper != SB_MEM_OP_NONE)
log_text(LOG_NOTICE, "%4.2f MB transferred (%4.2f MB/sec)\n",
total_bytes / megabyte,
total_bytes / megabyte / seconds);
break;
}
}
#ifdef HAVE_LARGE_PAGES
/* Allocate memory from HugeTLB pool */
void * hugetlb_alloc(size_t size)
{
int shmid;
void *ptr;
struct shmid_ds buf;
/* Align block size to my_large_page_size */
size = ((size - 1) & ~LARGE_PAGE_SIZE) + LARGE_PAGE_SIZE;
shmid = shmget(IPC_PRIVATE, size, SHM_HUGETLB | SHM_R | SHM_W);
if (shmid < 0)
{
log_errno(LOG_FATAL,
"Failed to allocate %d bytes from HugeTLB memory.", size);
return NULL;
}
ptr = shmat(shmid, NULL, 0);
if (ptr == (void *)-1)
{
log_errno(LOG_FATAL, "Failed to attach shared memory segment,");
shmctl(shmid, IPC_RMID, &buf);
return NULL;
}
/*
Remove the shared memory segment so that it will be automatically freed
after memory is detached or process exits
*/
shmctl(shmid, IPC_RMID, &buf);
return ptr;
}
#endif