Главная
Новости, анонсы ...


 Опыты
Статьи, исходники
и прочее





Чернопятов Е.А. Виртуалка "на скорую руку"



Возможно, многие сталкивались со следующей проблемой: есть данные, количество которых заранее неизвестно - но известно, что их может быть как очень много, так и совсем чуть-чуть, при этом предсказать их (данных) количество заведомо невозможно. Например, такая ситуация характерна для программных комплексов для инженерных расчетов. Пользователь может задать малый шаг расчета, и тогда данных будет много, а может и прервать расчет, и тогда их будет мало.

Для хранения этих данных можно воспользоваться двумя путями:

  • можно хранить все в памятии,
  • или можно сбрасывать насчитанные данные в файл.

Первый путь обеспечивает очень быстрый доступ к данным, однако размер хранимого массива данных ограничен объемом оперативной (и/или виртуальной) памяти. В тоже время на диске можно хранить данных существенно больше (учитывая размер существующих на сегодня дисков, измеряющийся десятками гигабайт). Однако это преимущество нивелируется низкой скоростью доступа (относительно скорости доступа к оперативной памяти, конечно). Это может быть критично в случае, например, необходимости визуализации данных.

Возникает естественное желание найти некий компромиссный вариант: если данных немного (например, менее 75% от доступной физической оперативной памяти компьютера) - хранить их в оперативке, если больше, то сбрасывать "излишек" на диск. В этом случае программист, так или иначе, задумывается о создании собственного менеджера виртуальной памяти (МВП). Однако реализация полноценной виртуалки сопряжена со значительными временными затратами как на написание и отладку кода, так и на тщательную оптимизацию алгоритмов. Существует ли какой-нибудь способ избежать трудоемкого программирования, и в тоже время реализовать эффективное выделение памяти ?

А зачем вообще связываться с написанием МВП? Разве Windows сама не в состоянии полноценно использовать свой собственный МВП? Ведь тот же самый файл подкачки есть ни что иное, как часть виртуальной памяти!

Можно, но, оказывается, не все так просто. Мои опыты показывают, что попытка размещения действительно большого объема данных только в оперативной памяти и переложение управления процессом выделения памяти только на МВП Windows всегда приводит к одному и тому же результату - выдачи сообщения о том, что системе не хватает виртуальной памяти и что файл подкачки должен быть увеличен. Система начинает тормозить, подвисать, и в конце концов либо приложение вылетает, либо пользователь сам его убивает. Нельзя же заставлять пользователя каждый раз пялиться на сообщения о проблемах операционной системы!

Предлагаемое решение, хотя и небесспорное, решает вышеприведенные проблемы, прозрачно позволяя, с одной стороны - максимально использовать оперативную память, и с другой стороны - сбрасывать данные на диск, когда оперативная память подходит к концу, при этом без выдачи сообщений операционной системы.

В WinAPI существует возможность создавать файлы (функцией CreateFile) с атрибутом FILE_ATTRIBUTE_TEMPORARY. Такой файл трактуется системой как временный. Интересное свойство созданного таким образом файла состоит в том, что такой временный файл используется системой как раз так, как нам надо: система старается (по возможности), хранить данные этого файла в оперативной памяти, не выгружая его на диск максимально долгое время. А если данных становится много, то система сама начинает сбрасывать их на диск. В этом случае все, о чем необходимо заботиться программисту, это о том, чтобы на диске, на котором был создан файл (да-да, именно так - файл создается на диске, но данные хранятся в памяти, и сбрасываются на диск только в случае необходимости) было достаточно места.

Доступ к такому файлу осуществляется обычными средствами WinAPI. Для удобства работы с данными можно написать класс, подобный классу CArray, но с соответсвующим внутренним устройством. Пример реализации базовой функциональности приведен ниже (проверка ошибок и сервисные функции опущены):

//////////////////////////////////////////////////////////////////////
// 
// Массив для хранения больших объемов данных
// Автор: Чернопятов Е.А (c) 2002-2003
//
//////////////////////////////////////////////////////////////////////

#include "Winbase.h"
#include "Windows.h"


template
class CVMArray: public CObject
{
private:
	__int64 vmFileSeek (HANDLE hf, __int64 distance, DWORD MoveMethod);

public:
	// Construction
	CVMArray();
	
	// Attributes
	int GetSize() const;
	int GetUpperBound() const;
	__int64 GetFileSize() const;
	
	// Operations
	// Clean up
	BOOL RemoveAll();
	
	// Accessing elements
	BOOL GetAt(__int64 nIndex, TYPE *Buffer) ;
	BOOL SetAt(__int64 nIndex, TYPE &newElement);
	
	// Potentially growing the array
	int Add(TYPE &newElement);
	
		
	// Implementation
protected:
	HANDLE m_Data;		// the actual file, storing array of data
	int m_nSize;		// # of elements (upperBound - 1)
	int m_nMaxSize;		// max allocated
	int m_nGrowBy;		// grow amount
	
public:
	~CVMArray();
	void Serialize(CArchive&);
#ifdef _DEBUG
	void Dump(CDumpContext&) const;
	void AssertValid() const;
#endif
};

/////////////////////////////////////////////////////////////////////////////
// CVMArray inline functions
template
AFX_INLINE __int64 CVMArray::vmFileSeek (HANDLE hf, __int64 distance, DWORD MoveMethod)
{
   LARGE_INTEGER li;

   li.QuadPart = distance*sizeof(TYPE);

   li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);

   if (GetLastError() != NO_ERROR)
   {
      li.QuadPart = -1;
   }

   return li.QuadPart;
};

/////////////////////////////////////////////////////////////////////////////
//
template
AFX_INLINE int CVMArray::GetSize() const
{ 
	return m_nSize; 
}

/////////////////////////////////////////////////////////////////////////////
//
template
AFX_INLINE int CVMArray::GetUpperBound() const
{ 
	return m_nSize-1; 
}

/////////////////////////////////////////////////////////////////////////////
//
template
AFX_INLINE __int64 CVMArray::GetFileSize() const
{
	return ::GetFileSize(m_Data,NULL);
}

/////////////////////////////////////////////////////////////////////////////
//
template
AFX_INLINE BOOL CVMArray::RemoveAll()
{ 	
	m_nSize=0;
	
	if (vmFileSeek(m_Data,0,FILE_BEGIN)==-1) //failure
	{
		ASSERT(0); 
		return FALSE;
	}
	return SetEndOfFile(m_Data);	
}

/////////////////////////////////////////////////////////////////////////////
//
template
AFX_INLINE BOOL CVMArray::GetAt(__int64 nIndex, TYPE *Buffer) 
{ 
	DWORD bytesread;

	ASSERT(nIndex >= 0 && nIndex < m_nSize);
	
	if (vmFileSeek(m_Data,nIndex,FILE_BEGIN)==-1) //failure
	{
		ASSERT(0); 
		return FALSE;
	}
	
	return ReadFile(m_Data,Buffer,sizeof(TYPE),&bytesread,NULL);
}

/////////////////////////////////////////////////////////////////////////////
//
template
AFX_INLINE BOOL CVMArray::SetAt(__int64 nIndex, TYPE &newElement)
{ 
	DWORD byteswritten;

	ASSERT(nIndex >= 0 && nIndex < m_nSize);

	if (vmFileSeek(m_Data,nIndex,FILE_BEGIN)==-1) //failure
	{
		ASSERT(0); 
		return FALSE;
	}
	
	return WriteFile(m_Data,&newElement,sizeof(TYPE),&byteswritten,NULL);
}

/////////////////////////////////////////////////////////////////////////////
//
template
AFX_INLINE int CVMArray::Add(TYPE &newElement)
{ 
	int nIndex = m_nSize;
	m_nSize++;
	SetAt(nIndex, newElement);
	return nIndex; 
}

/////////////////////////////////////////////////////////////////////////////
//
template
CVMArray::CVMArray()
{
	m_Data = NULL;
	m_nSize = m_nMaxSize = m_nGrowBy = 0;

	TCHAR szBuffer[_MAX_PATH];
	TCHAR szTempFileName[_MAX_PATH];

	VERIFY (GetTempPath(_MAX_PATH, szBuffer));
	VERIFY (GetTempFileName(szBuffer,"vmmem",0,szTempFileName));
	
	m_Data = CreateFile(szTempFileName, GENERIC_READ|GENERIC_WRITE,
		0,NULL,CREATE_ALWAYS,
		FILE_ATTRIBUTE_NOT_CONTENT_INDEXED|
		FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE,
		NULL);
	ASSERT (m_Data);

}

/////////////////////////////////////////////////////////////////////////////
//
template
CVMArray::~CVMArray()
{
	ASSERT_VALID(this);
	
	CloseHandle(m_Data);
}


P.S.
А как же CMemFile? А он хранится только в оперативной памяти и использование его не дает никаких преимуществ.




Оглавление

Последние изменения от 09.08.2013




e-mail:  Yegor A. Blackheel

Поиск по сайту с помощью Yandex

 www.guestbook.ru - лучший сервер гостевых книг