/*
* Copyright (C) 2015 - 2018 Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice(s),
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice(s),
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 HOLDER(S) 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.
*/
#pragma once
#include "memkind.h"
#include "Allocator.hpp"
#include "StandardAllocatorWithTimer.hpp"
#include "VectorIterator.hpp"
#include "Configuration.hpp"
#include "JemallocAllocatorWithTimer.hpp"
#include "MemkindAllocatorWithTimer.hpp"
#include "HBWmallocAllocatorWithTimer.hpp"
#include "Numastat.hpp"
#include "PmemMockup.hpp"
#include <vector>
#include <assert.h>
#include <string.h>
#include <map>
class AllocatorFactory
{
public:
//Allocator initialization statistics
struct initialization_stat {
float total_time; //Total time of initialization.
float ref_delta_time; //Delta Time.
unsigned allocator_type;
std::vector<float> memory_overhead; //Memory overhead per numa node.
};
AllocatorFactory()
{
memkind_allocators[AllocatorTypes::MEMKIND_DEFAULT] = MemkindAllocatorWithTimer(
MEMKIND_DEFAULT, AllocatorTypes::MEMKIND_DEFAULT);
memkind_allocators[AllocatorTypes::MEMKIND_REGULAR] = MemkindAllocatorWithTimer(
MEMKIND_REGULAR, AllocatorTypes::MEMKIND_REGULAR);
memkind_allocators[AllocatorTypes::MEMKIND_HBW] = MemkindAllocatorWithTimer(
MEMKIND_HBW, AllocatorTypes::MEMKIND_HBW);
memkind_allocators[AllocatorTypes::MEMKIND_INTERLEAVE] =
MemkindAllocatorWithTimer(MEMKIND_INTERLEAVE,
AllocatorTypes::MEMKIND_INTERLEAVE);
memkind_allocators[AllocatorTypes::MEMKIND_HBW_INTERLEAVE] =
MemkindAllocatorWithTimer(MEMKIND_HBW_INTERLEAVE,
AllocatorTypes::MEMKIND_HBW_INTERLEAVE);
memkind_allocators[AllocatorTypes::MEMKIND_HBW_PREFERRED] =
MemkindAllocatorWithTimer(MEMKIND_HBW_PREFERRED,
AllocatorTypes::MEMKIND_HBW_PREFERRED);
memkind_allocators[AllocatorTypes::MEMKIND_HUGETLB] = MemkindAllocatorWithTimer(
MEMKIND_HUGETLB, AllocatorTypes::MEMKIND_HUGETLB);
memkind_allocators[AllocatorTypes::MEMKIND_GBTLB] = MemkindAllocatorWithTimer(
MEMKIND_GBTLB, AllocatorTypes::MEMKIND_GBTLB);
memkind_allocators[AllocatorTypes::MEMKIND_HBW_HUGETLB] =
MemkindAllocatorWithTimer(MEMKIND_HBW_HUGETLB,
AllocatorTypes::MEMKIND_HBW_HUGETLB);
memkind_allocators[AllocatorTypes::MEMKIND_HBW_PREFERRED_HUGETLB] =
MemkindAllocatorWithTimer(MEMKIND_HBW_PREFERRED_HUGETLB,
AllocatorTypes::MEMKIND_HBW_PREFERRED_HUGETLB);
memkind_allocators[AllocatorTypes::MEMKIND_HBW_GBTLB] =
MemkindAllocatorWithTimer(MEMKIND_HBW_GBTLB, AllocatorTypes::MEMKIND_HBW_GBTLB);
memkind_allocators[AllocatorTypes::MEMKIND_HBW_PREFERRED_GBTLB] =
MemkindAllocatorWithTimer(MEMKIND_HBW_PREFERRED_GBTLB,
AllocatorTypes::MEMKIND_HBW_PREFERRED_GBTLB);
memkind_allocators[AllocatorTypes::MEMKIND_PMEM] = MemkindAllocatorWithTimer(
MEMKIND_PMEM_MOCKUP, AllocatorTypes::MEMKIND_PMEM);
}
//Get existing allocator without creating new.
//The owner of existing allocator is AllocatorFactory object.
Allocator *get_existing(unsigned type)
{
switch(type) {
case AllocatorTypes::STANDARD_ALLOCATOR:
return &standard_allocator;
case AllocatorTypes::JEMALLOC:
return &jemalloc;
case AllocatorTypes::HBWMALLOC_ALLOCATOR:
return &hbwmalloc_allocator;
default: {
if(memkind_allocators.count(type))
return &memkind_allocators[type];
assert(!"'type' out of range!");
}
}
}
initialization_stat initialize_allocator(Allocator &allocator)
{
size_t initial_size = 512;
float before_node1 = Numastat::get_total_memory(0);
float before_node2 = Numastat::get_total_memory(1);
initialization_stat stat = {0};
//malloc
memory_operation malloc_data = allocator.wrapped_malloc(initial_size);
stat.total_time += malloc_data.total_time;
//realloc
memory_operation realloc_data = allocator.wrapped_realloc(malloc_data.ptr, 256);
allocator.wrapped_free(realloc_data.ptr);
stat.total_time += realloc_data.total_time;
//calloc
memory_operation calloc_data = allocator.wrapped_calloc(initial_size, 1);
allocator.wrapped_free(calloc_data.ptr);
stat.total_time += calloc_data.total_time;
stat.allocator_type = allocator.type();
//calc memory overhead
stat.memory_overhead.push_back(Numastat::get_total_memory(0) - before_node1);
stat.memory_overhead.push_back(Numastat::get_total_memory(1) - before_node2);
return stat;
}
initialization_stat initialize_allocator(unsigned type)
{
return initialize_allocator(*get_existing(type));
}
//Calc percent delta between reference value and current value.
float calc_ref_delta(float ref_value, float value)
{
return ((value / ref_value) - 1.0) * 100.0;
}
//Test initialization performance over available allocators.
//Return statistics.
std::vector<initialization_stat> initialization_test()
{
std::vector<initialization_stat> stats;
initialization_stat stat;
stat = initialize_allocator(standard_allocator);
float ref_time = stat.total_time;
stats.push_back(stat);
//Loop over available allocators to call initializer and compute stats.
for (unsigned i=1; i<AllocatorTypes::NUM_OF_ALLOCATOR_TYPES; i++) {
stat = initialize_allocator(*get_existing(i));
stat.ref_delta_time = calc_ref_delta(ref_time, stat.total_time);
stats.push_back(stat);
}
return stats;
}
VectorIterator<Allocator *> generate_random_allocator_calls(int num, int seed,
TypesConf allocator_calls)
{
srand(seed);
std::vector<Allocator *> allocators_calls;
for (int i=0; i<num; i++) {
int index;
do {
index = (rand() % (AllocatorTypes::NUM_OF_ALLOCATOR_TYPES));
} while(!allocator_calls.is_enabled(index));
allocators_calls.push_back(get_existing(index));
}
return VectorIterator<Allocator *>::create(allocators_calls);
}
//Return kind to the corresponding AllocatorTypes enum specified in argument.
memkind_t get_kind_by_type(unsigned type)
{
if(memkind_allocators.count(type))
return memkind_allocators[type].get_kind();
assert(!"'type' out of range!");
}
private:
StandardAllocatorWithTimer standard_allocator;
JemallocAllocatorWithTimer jemalloc;
HBWmallocAllocatorWithTimer hbwmalloc_allocator;
std::map<unsigned, MemkindAllocatorWithTimer> memkind_allocators;
};