Практическое руководство. Использование функций Alloc и Free для повышения производительности памяти
Этот документ показывает, как использовать concurrency::Alloc и concurrency::Free функции для повышения производительности памяти.В нем сравнивается время, необходимое для параллельного изменения порядка элементов массива на обратный для трех различных типов, каждый из которых задает операторы new и delete.
Функции Alloc и Free наиболее полезны, когда несколько потоков часто вызывают обе функции Alloc и Free.Среда выполнения хранит отдельный кэш памяти для каждого потока; поэтому среда выполнения управляет памятью без использования блокировок или барьеров в памяти.
Пример
В следующем примере показаны три типа, каждый из которых задает операторы new и delete.В классе new_delete используются глобальные операторы new и delete, в классе malloc_free используются функции malloc и free среды выполнения C, а в классе Alloc_Free используются функции Alloc и Free среды выполнения с параллелизмом.
// A type that defines the new and delete operators. These operators
// call the global new and delete operators, respectively.
class new_delete
{
public:
static void* operator new(size_t size)
{
return ::operator new(size);
}
static void operator delete(void *p)
{
return ::operator delete(p);
}
int _data;
};
// A type that defines the new and delete operators. These operators
// call the C Runtime malloc and free functions, respectively.
class malloc_free
{
public:
static void* operator new(size_t size)
{
return malloc(size);
}
static void operator delete(void *p)
{
return free(p);
}
int _data;
};
// A type that defines the new and delete operators. These operators
// call the Concurrency Runtime Alloc and Free functions, respectively.
class Alloc_Free
{
public:
static void* operator new(size_t size)
{
return Alloc(size);
}
static void operator delete(void *p)
{
return Free(p);
}
int _data;
};
В следующем примере показаны функции swap и reverse_array.Функция swap меняет местами содержимое массива с указанными индексами.Она выделяет память для временной переменной из кучи.Функция reverse_array создает большой массив и вычисляет время, необходимое для того, чтобы несколько раз изменить порядок элементов этого массива на обратный в параллельном режиме.
// Exchanges the contents of a[index1] with a[index2].
template<class T>
void swap(T* a, int index1, int index2)
{
// For illustration, allocate memory from the heap.
// This is useful when sizeof(T) is large.
T* temp = new T;
*temp = a[index1];
a[index1] = a[index2];
a[index2] = *temp;
delete temp;
}
// Computes the time that it takes to reverse the elements of a
// large array of the specified type.
template <typename T>
__int64 reverse_array()
{
const int size = 5000000;
T* a = new T[size];
__int64 time = 0;
const int repeat = 11;
// Repeat the operation several times to amplify the time difference.
for (int i = 0; i < repeat; ++i)
{
time += time_call([&] {
parallel_for(0, size/2, [&](int index)
{
swap(a, index, size-index-1);
});
});
}
delete[] a;
return time;
}
В следующем примере показана функция wmain, вычисляющая время, необходимое функции reverse_array для работы с типами new_delete, malloc_free и Alloc_Free, каждый из которых использует различные схемы выделения памяти.
int wmain()
{
// Compute the time that it takes to reverse large arrays of
// different types.
// new_delete
wcout << L"Took " << reverse_array<new_delete>()
<< " ms with new/delete." << endl;
// malloc_free
wcout << L"Took " << reverse_array<malloc_free>()
<< " ms with malloc/free." << endl;
// Alloc_Free
wcout << L"Took " << reverse_array<Alloc_Free>()
<< " ms with Alloc/Free." << endl;
}
Полный пример кода выглядит следующим образом.
// allocators.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Calls the provided work function and returns the number of milliseconds
// that it takes to call that function.
template <class Function>
__int64 time_call(Function&& f)
{
__int64 begin = GetTickCount();
f();
return GetTickCount() - begin;
}
// A type that defines the new and delete operators. These operators
// call the global new and delete operators, respectively.
class new_delete
{
public:
static void* operator new(size_t size)
{
return ::operator new(size);
}
static void operator delete(void *p)
{
return ::operator delete(p);
}
int _data;
};
// A type that defines the new and delete operators. These operators
// call the C Runtime malloc and free functions, respectively.
class malloc_free
{
public:
static void* operator new(size_t size)
{
return malloc(size);
}
static void operator delete(void *p)
{
return free(p);
}
int _data;
};
// A type that defines the new and delete operators. These operators
// call the Concurrency Runtime Alloc and Free functions, respectively.
class Alloc_Free
{
public:
static void* operator new(size_t size)
{
return Alloc(size);
}
static void operator delete(void *p)
{
return Free(p);
}
int _data;
};
// Exchanges the contents of a[index1] with a[index2].
template<class T>
void swap(T* a, int index1, int index2)
{
// For illustration, allocate memory from the heap.
// This is useful when sizeof(T) is large.
T* temp = new T;
*temp = a[index1];
a[index1] = a[index2];
a[index2] = *temp;
delete temp;
}
// Computes the time that it takes to reverse the elements of a
// large array of the specified type.
template <typename T>
__int64 reverse_array()
{
const int size = 5000000;
T* a = new T[size];
__int64 time = 0;
const int repeat = 11;
// Repeat the operation several times to amplify the time difference.
for (int i = 0; i < repeat; ++i)
{
time += time_call([&] {
parallel_for(0, size/2, [&](int index)
{
swap(a, index, size-index-1);
});
});
}
delete[] a;
return time;
}
int wmain()
{
// Compute the time that it takes to reverse large arrays of
// different types.
// new_delete
wcout << L"Took " << reverse_array<new_delete>()
<< " ms with new/delete." << endl;
// malloc_free
wcout << L"Took " << reverse_array<malloc_free>()
<< " ms with malloc/free." << endl;
// Alloc_Free
wcout << L"Took " << reverse_array<Alloc_Free>()
<< " ms with Alloc/Free." << endl;
}
На четырехпроцессорном компьютере этот пример создает следующие выходные данные.
Took 2031 ms with new/delete.
Took 1672 ms with malloc/free.
Took 656 ms with Alloc/Free.
В этом примере тип, использующий функции Alloc и Free, обеспечивает наивысшую производительность памяти, так как функции Alloc и Free оптимизированы для частого выделения и освобождения блоков памяти из нескольких потоков.
Компиляция кода
Скопируйте код и вставьте его в проект Visual Studio или вставьте его в файл с именем allocators.cpp и запустите следующую команду в окне командной строки Visual Studio.
cl.exe /EHsc allocators.cpp