Войти

Показать полную графическую версию : MemoryMappedFile & ReadFile


Sidewalker
19-11-2011, 09:49
Добрый день.

В общем задание - сравнить скорость чтения отображенного в ОЗУ файла и просто напрямую с диска через ReadFile.
("Сравнить среднее время доступа к данным в файле при использовании метода отображения файла на адресное пространство процесса и при использовании традиционного доступа к файлу с использованием системного вызова ReadFile.")

Погуглил погуглил и MSDN и пр. мне сказали что отображением файла занимается класс MemoryMappingFile. Вроде прогу написал, всё работает. НО ReadFile (который как я понял считывает с харда напрямую?) работает БЫСТРЕЕ чем в случае отображения файла.


using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices; // для вызова функций из WinAPI
using System.Timers;

namespace mapping1
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
unsafe static extern bool ReadFile
(
IntPtr hFile,
void* lpBuffer,
int nNumberOfBytesToRead,
ref int lpNumberOfBytesRead,
IntPtr lpOverlapped
);

struct s1 { public int[] mas1;} // 1 MB
struct s20 { public int[] mas2;} // 20 MB
struct s512 { public int[] mas3;} // 256 MB

static void Main()
{
s1 Sc1;
Sc1.mas1 = new int[262144]; // 1 MB
s20 Sc2;
Sc2.mas2 = new int[5242880]; // 20 MB
s512 Sc3;
Sc3.mas3 = new int[67108864]; // 256 MB

long offset1 = 0x0000000; // смещение для CreateViewAccessor
long length1 = 0x0100000; // 1 mb

long offset2 = 0x00A00000; // 10 MB
long length2 = 0x01400000; // 20 MB

long offset3 = 0x10000000; // 256 MB
long length3 = 0x10000000; // 256 MB

long time1 = 0;
long time2 = 0;
long time3 = 0;

Console.ReadKey();

using (var mmf1 = MemoryMappedFile.CreateFromFile(@"C:\Users\xxx\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, "file1"))
{
// Create a random access view, from the 256th megabyte (the offset)
// to the 512th megabyte (the offset plus length).
using (var accessor1 = mmf1.CreateViewAccessor(offset1, length1))
{
Console.WriteLine("Mapping and reading from mapped file...");
Console.WriteLine("Mapped to RAM: " + (accessor1.Capacity / (1024 * 1024)) + " megabytes");
long a1 = DateTime.Now.Ticks;
accessor1.ReadArray<int>(0, Sc3.mas3, 0, 262144); // 1 mb
long a2 = DateTime.Now.Ticks;
time1 = (a2 - a1) / 10000; // milliseconds
Console.WriteLine("Reading time: " + time1 + " milliseconds\n");
}
mmf1.Dispose(); // освобождает все ресурсы, использумые mmf1

}

using (var mmf2 = MemoryMappedFile.CreateFromFile(@"C:\Users\xxx\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, "file1"))
{
using (var accessor1 = mmf2.CreateViewAccessor(offset2, length2))
{
Console.WriteLine("Mapped to RAM: " + (accessor1.Capacity / (1024 * 1024)) + " megabytes");
long a1 = DateTime.Now.Ticks;
accessor1.ReadArray<int>(0, Sc2.mas2, 0, 5242880); // 20 mb
long a2 = DateTime.Now.Ticks;
time2 = (a2 - a1) / 10000; // milliseconds

Console.WriteLine("Reading time: " + time2 + " milliseconds\n");
}
mmf2.Dispose(); // освобождает все ресурсы, использумые mmf2

}

using (var mmf3 = MemoryMappedFile.CreateFromFile(@"C:\Users\xxx\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, "file1"))
{
using (var accessor1 = mmf3.CreateViewAccessor(offset3, length3))
{
Console.WriteLine("Mapped to RAM: " + (accessor1.Capacity / (1024 * 1024)) + " megabytes");
long a1 = DateTime.Now.Ticks;
accessor1.ReadArray<int>(0, Sc3.mas3, 0, 67108864); // 256 mb
long a2 = DateTime.Now.Ticks;
time3 = (a2 - a1) / 10000; // milliseconds

Console.WriteLine("Reading time: " + time3 + " milliseconds\n");
}
mmf3.Dispose(); // освобождает все ресурсы, использумые mmf3

}

// принудительная сборка мусора
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.GetTotalMemory(true);

Console.ReadKey();

// part2 : directly from hdd

long time_1 = 0;
long time_2 = 0;
long time_3 = 0;

int length_1 = 262144;
int length_2 = 5242880;
int length_3 = 67108864;
length_1 *= sizeof(int);
length_2 *= sizeof(int);
length_3 *= sizeof(int);

Console.WriteLine("Reading from HDD...");
using (var fs1 = File.Open(@"C:\Users\xxx\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, FileAccess.Read))
{
unsafe
{
fixed (int* pointerForArray = Sc1.mas_1)
{
long a1 = DateTime.Now.Ticks;
ReadFile(fs1.Handle, pointerForArray, length_1, ref length_1, IntPtr.Zero);
long a2 = DateTime.Now.Ticks;
time_1 = (a2 - a1) / 10000; // milliseconds
}
Console.WriteLine("First block readed success. Time:" + time_1);

fixed (int* pointerForArray = Sc2.mas_2)
{
long a1 = DateTime.Now.Ticks;
ReadFile(fs1.Handle, pointerForArray, length_2, ref length_2, IntPtr.Zero);
long a2 = DateTime.Now.Ticks;
time_2 = (a2 - a1) / 10000; // milliseconds
}
Console.WriteLine("Second block readed success. Time:" + time_2);

fixed (int* pointerForArray = Sc3.mas_3)
{
long a1 = DateTime.Now.Ticks;
ReadFile(fs1.Handle, pointerForArray, length_3, ref length_3, IntPtr.Zero);
long a2 = DateTime.Now.Ticks;
time_3 = (a2 - a1) / 10000; // milliseconds
}
Console.WriteLine("Third block readed success. Time:" + time_3);
}

fs1.Dispose();
fs1.Close();
}

Console.WriteLine("Statistics:\nwith mapping (from RAM): " + time1 + " " + time2 + " " + time3 +
"\nwithout mapping (from HDD): " + time_1 + " " + time_2 + " " + time_3);

Console.ReadKey();
}
}
}


Mapping and reading from mapped file...
Mapped to RAM: 1 megabytes
Reading time: 3 milliseconds

Mapped to RAM: 20 megabytes
Reading time: 70 milliseconds

Mapped to RAM: 256 megabytes
Reading time: 836 milliseconds

Reading from HDD...
First block readed success. Time:0
Second block readed success. Time:12
Third block readed success. Time:173
Statistics:
with mapping (from RAM): 3 70 836
without mapping (from HDD): 0 12 173

WTF?.... Вроде сборщик мусора принудительно запускал, но память не очищается - до первого ReadKey прога весит в озу 19 мб, до второго ReadKey 300 мб, до третьего (в итоге) - примерно 570 Мб.

lxa85
19-11-2011, 10:25
WTF?. »
А что ты хотел? Все правильно работает. Время мапирования файла в память больше, нежели просто чтение.
В общем задание - сравнить скорость чтения отображенного в ОЗУ файла и просто напрямую с диска через ReadFile.
("Сравнить среднее время доступа к данным в файле при использовании метода отображения файла на адресное пространство процесса и при использовании традиционного доступа к файлу с использованием системного вызова ReadFile.")
Погуглил погуглил и MSDN и пр. мне сказали что отображением файла занимается класс MemoryMappingFile. Вроде прогу написал, всё работает. НО ReadFile (который как я понял считывает с харда напрямую?) работает БЫСТРЕЕ чем в случае отображения файла. »
Вот теперь перечитай задание еще раз и внимательно (полезная вещь, сам пользуюсь :) )
Что тебя спрашивают? Правильно!
"Сравнить среднее время доступа к данным в файле(1) при использовании метода отображения файла(2) на адресное пространство процесса и при использовании традиционного доступа к файлу(3) с использованием системного вызова ReadFile."

Объясни мне, какое отношение написанный код имеет к скорости чтения файлов и мапирования их в память?
Отвечу - никакого!
То, что ты написал - подготовительный этап тестирования. Да, мапирование дольше, т.к. там
1 - чтение файла с медленного HDD, 2 - запись данных в ОЗУ (+накладные расходы, выделения памяти, менеджера памяти и т.д.). Чтение - это просто чтение.
----
Вот сейчас, когда у тебя файл на hdd и его дубликат в памяти, проводи тесты на скорость линейного доступа, случайного доступа. Получишь графики - объясняй их форму, характер и т.д.
Вот это - курсовая. А то что у тебя сейчас есть - это подготовительный этап, важный, но подготовительный. От него зависит, как именно файл будет расположен в памяти, и как файл будет расположен на hdd. Это исходные данные.
----
Беглый просмотр кода, позволяет тебя поздравить с выполнением этого этапа. Теперь самое интересное - тесты на скорость доступа.
Успехов! :)

Sidewalker
19-11-2011, 10:31
Да, мапирование дольше, т.к. там
1 - чтение файла с медленного HDD, 2 - запись данных в ОЗУ (+накладные расходы, выделения памяти, менеджера памяти и т.д.). »

long a1 = DateTime.Now.Ticks;
accessor1.ReadArray<int>(0, Sc3.mas3, 0, 262144); // 1 mb
long a2 = DateTime.Now.Ticks;
time1 = (a2 - a1) / 10000; // milliseconds »

Что насчёт того что я засекаю время именно копирования данных из озу (отображенный файл) в озу (массив в структуре) ?? Мне казалось что чтение файла с HDD выполняется в
var mmf1 = MemoryMappedFile.CreateFromFile(@"C:\Users\xxx\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, "file1")) »


Получишь графики - объясняй их форму, характер и т.д. »
Мне нужна МИКРОстатистика, не более того, без графиков и проч. Только небольшое сравнение.

проводи тесты на скорость линейного доступа, случайного доступа »
Я разве не этим занимаюсь в
long a1 = DateTime.Now.Ticks;
accessor1.ReadArray<int>(0, Sc3.mas3, 0, 262144); // 1 mb
long a2 = DateTime.Now.Ticks;
time1 = (a2 - a1) / 10000; // milliseconds
? Чем не тест?

lxa85
19-11-2011, 10:55
ок.
using (var mmf1 = MemoryMappedFile.CreateFromFile(@"C:\Users\RazoR\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, "file1"))
{
// Create a random access view, from the 256th megabyte (the offset)
// to the 512th megabyte (the offset plus length).
using (var accessor1 = mmf1.CreateViewAccessor(offset1, length1))
{
Console.WriteLine("Mapping and reading from mapped file...");
Console.WriteLine("Mapped to RAM: " + (accessor1.Capacity / (1024 * 1024)) + " megabytes");
long a1 = DateTime.Now.Ticks;
accessor1.ReadArray<int>(0, Sc3.mas3, 0, 262144); // 1 mb
long a2 = DateTime.Now.Ticks;
time1 = (a2 - a1) / 10000; // milliseconds
Console.WriteLine("Reading time: " + time1 + " milliseconds\n");
}
mmf1.Dispose(); // освобождает все ресурсы, использумые mmf1 »
Вот этот кусок распиши по шагам.
Что куда попадает, в какой структурной форме (уровнем асбтракции) и т.п.
Т.е. у меня сейчас предположение, что тут "навёрнута" слишком дурацкая конструкция со множеством "вложенностей"
(var mmf1 = MemoryMappedFile.CreateFromFile(@"C:\Users\RazoR\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, "file1")
Когда это выполняется?
(var accessor1 = mmf1.CreateViewAccessor(offset1, length1) Что это? Еще одна ссылка на внешний объект?
Где в коде можно поставить точку и сказать, что "Вот. Вот здесь мы получили "чистую" копию файла в памяти"?
Проверь, что не идет динамического обращения к дисковой подсистеме.
Постарайся вообще убрать всякую оптимизацию кода компилятором. А то кто эти компиляторы .NET знает, вполне могли оптимизировать.
accessor1.ReadArray<int>(0, Sc3.mas3, 0, 262144); // 1 mb
Это еще что такое? Причем здесь Sc3.mas3 ? Ладно, спишем, как опечатку.
----
Мне нужна МИКРОстатистика »
Ага, это ты на защите расскажешь. :)

Sidewalker
19-11-2011, 14:48
Когда это выполняется? »
Всегда.
Что это? Еще одна ссылка на внешний объект? »
CreateFromFile(String, FileMode, String) - Создает из файла на диске размещенный в памяти файл с заданным режимом доступа и именем.
CreateViewAccessor() - Создает метод MemoryMappedViewAccessor, который соответствует представлению размещенного в памяти файла.
Внизу ссылки - пример. http://msdn.microsoft.com/ru-ru/library/system.io.memorymappedfiles.memorymappedfile.aspx
Так что насчёт "дурацкая конструкция" - всё к мелкософту.

Проверь, что не идет динамического обращения к дисковой подсистеме. »
Не понял тебя. Что значит проверить? Мне все потоки харда палить или просто на лампочку системника посмотреть? Если да, то прога слишком быстро выполняется чтобы заметить))

Ладно, спишем, как опечатку. »
Так оно и есть. Хз как закралась) Конечно же там Sc1.mas1. Именовал так для удобства.

Ага, это ты на защите расскажешь. »
У нас защита не жесткая будет) Но сделать добросовестно конечно надо.

lxa85
19-11-2011, 15:22
Когда это выполняется? »
Всегда. »
До вызова процедуры, во время вызова процедуры, во время выполнения процедуры?Проверь, что не идет динамического обращения к дисковой подсистеме. »
Меня интересует. Идет ли обращение к HDD во время выполнения процедуры?
У меня вопрос тот же. Поставить пальцем точку в листинге программы и сказать, что "От сих мы работаем только с ОЗУ". И разумеется обосновать свое решение.
Хорошо бы вообще отключить физический доступ к носителю, после того, как файл был скопирован в память. BTW имеет ли разницу слова "скопировать файл в память" и "смапировал/отобразил файл в память"?
----
На предзащите гораздо интереснее смотреть толковый график, нежели всматриваться в микро(очень маленькую? :))статистику с колонками цифр.

Sidewalker
19-11-2011, 19:29
До вызова процедуры, во время вызова процедуры, во время выполнения процедуры? »
До. То, что внутри () после using, выполняется до его тела. Собственно CreateFileMapping как раз и отображает ( =копирует) в озу часть (или весь, у меня часть) файла с другого носителя.

Поставить пальцем точку в листинге программы и сказать, что "От сих мы работаем только с ОЗУ". »

using (var mmf1 = MemoryMappedFile.CreateFromFile(@"C:\Users\xxx\Documents\Visual Studio 2010\Projects\sizetests1\sizetests1\bin\Debug\Test.data", FileMode.Open, "file1"))
{ » ОТ СИХ. И до закрытия фигурной скобки using'а.

На предзащите гораздо интереснее смотреть толковый график, нежели всматриваться в микро(очень маленькую? )статистику с колонками цифр. »
Это не диплом, у нас у курсовых нет предзащит) Потом мб ещё другими объемами поиграюсь, а так хватит. Тем более для отображения ( = мэппинга) в ОЗУ требуется непрерывный(!) свободный кусок. А большого обычно такового нет.

В общем. Проблема решилась. Дело было в том, что при таком
accessor1.ReadArray<int>(0, Sc2.mas2, 0, 5242880); // 20 mb »
способе из озу считывается и копируется в массив поэлементно (ОБЕ операции), т.е. считалось 4 байта, скопировалось ( =записалось в озу в массив) в элемент, и потом так же для следующего. Всё бы ничего, НО ReadFile читает СРАЗУ весь блок с харда, а потом просто копирует его в массив. Соответственно это выполняется примерно с такой же скоростью как и в случае mapping'а, порой и быстрее. Т.к. отказываться от такой удобной обертки как MemoryMappedFile не хотелось и суперпроизводительность не была целью, решил исправить проблему путем считывания ReadFile по 4 байта в цикле.



for (i = 0; i < length_1; i++)
{
ReadFile(fs1.Handle, pointerForArray+i, 4, ref plen_1, IntPtr.Zero);
}




© OSzone.net 2001-2012