Поделиться через


Практическое руководство. Использование функций 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

См. также

Ссылки

Функция Alloc

Функция Free

Основные понятия

Функции управления памятью