![](https://habrastorage.org/getpro/geektimes/post_images/2f3/23c/3d4/2f323c3d407fbe3034fcf3ef0525f12d.jpg)
Каталог:
Один
Два
Три
μTorrent
Это, пожалуй, самая забавная часть. При очевидно высоких навыках программирования, авторы либо не читали, либо не поняли вот этот документ. Вот что происходит с дефолтными настройками:
![](https://habrastorage.org/storage/5686915b/8d9634fa/6765069e/d1031d51.png)
Поток 4600 открывает для раздачи файл с отключенным системным кешем. Причем все выполняется без снижения приоритета:
![](https://habrastorage.org/storage/d5c29859/9e8e48c1/b1b843db/dd00d687.png)
Я не пользуюсь торрентами и файлов для раздачи у меня не оказалось, да и каналы на отдачу в местных широтах довольно узкие, так что мне пришлось написать «эмулятор» мюторрента. Для начала «старая версия», не выключавшая кеширование (написал на сишарпе, потому что не у всех может оказаться установленная Visual Studio):
using System; using System.IO; using System.Threading; namespace abuseio { class Program { static void Main(string[] args) { for (var i = 0; i < 10; i++) { (new Thread(() => { var file = File.OpenRead(args[0]); var chunkSize = 16384; var chunks = (int)(file.Length / chunkSize); var rnd = new Random(); var buffer = new byte[chunkSize]; while (true) { long index = rnd.Next(chunks); file.Seek(index * chunkSize, SeekOrigin.Begin); file.Read(buffer, 0, chunkSize); } })).Start(); } } } }
В несколько потоков читаем случайные места предоставленного файла (лучше использовать что нибудь большое). Несколько минут после старта:
![](https://habrastorage.org/storage/948ea25f/a7a959c8/933894bb/fae9cddc.png)
Видим довольно много кешированных файлов. Несколько часов после старта:
![](https://habrastorage.org/storage/d479bc1b/d1ca5877/eff8389a/7235dc04.png)
В памяти остался только хром (в основном потому, что я им активно пользовался) и ругие Active страницы файлов, означающие недавнее использование. В Standby остались в основном те самые priority 7 страницы:
Вот, к примеру, небольшая часть shell32.dll:
![](https://habrastorage.org/storage/ced0757c/a8e9e6bf/76fb0add/1d790b00.png)
Ну и «общий вид»:
![](https://habrastorage.org/storage/4ea4111d/85988324/af429961/9558c244.png)
Почти все закешировалось на пятый уровень, вытеснив всех, кто находился там до этого. Более того, из-за частых обращений к «раздаваемым» файлам, суперфетч динамически повысил приоритет частей этих файлов и вытеснил часть кеша даже оттуда. Короче, все плохо.
Вот «эмулятор» нового мюторрента:
using System; using System.Runtime.InteropServices; using System.IO; using System.Threading; using Microsoft.Win32.SafeHandles; namespace abuseio { class Program { [Flags] enum FileAttributes : uint { Readonly = 0x00000001, Hidden = 0x00000002, System = 0x00000004, Directory = 0x00000010, Archive = 0x00000020, Device = 0x00000040, Normal = 0x00000080, Temporary = 0x00000100, SparseFile = 0x00000200, ReparsePoint = 0x00000400, Compressed = 0x00000800, Offline = 0x00001000, NotContentIndexed = 0x00002000, Encrypted = 0x00004000, Write_Through = 0x80000000, Overlapped = 0x40000000, NoBuffering = 0x20000000, RandomAccess = 0x10000000, SequentialScan = 0x08000000, DeleteOnClose = 0x04000000, BackupSemantics = 0x02000000, PosixSemantics = 0x01000000, OpenReparsePoint = 0x00200000, OpenNoRecall = 0x00100000, FirstPipeInstance = 0x00080000 } [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern SafeFileHandle CreateFile( string fileName, [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess, [MarshalAs(UnmanagedType.U4)] FileShare fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] FileAttributes flags, IntPtr template); static void Main(string[] args) { for (var i = 0; i < 10; i++) { (new Thread(() => { var fileHandle = CreateFile(args[0], FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.NoBuffering, IntPtr.Zero); var file = new FileStream(fileHandle, FileAccess.Read); var chunkSize = 16384; var chunks = (int)(file.Length / chunkSize); var rnd = new Random(); var buffer = new byte[chunkSize]; while (true) { long index = rnd.Next(chunks); file.Seek(index * chunkSize, SeekOrigin.Begin); file.Read(buffer, 0, chunkSize); } })).Start(); } } } }
Отключаем буферизацию и делаем все «кеширование» сами (кавычки, потому что сложно назвать нормальным решение управлять физической памятью из пользовательского режима):
1. За счет все еще неоправданно высокого приоритета ввода-вывода это мешает нормальной работе с компьютером
2. Отсутствует глобальный кеш. Если кому то еще надо обратиться к файлу — кешированная информация продублируется еще и в системном кеше
3. Существуют проблемы с размером доступного кеша
4. Last but not least, это не особо помогает. Так как если мюторрент будет выделять гигабайты памяти, он окажется первым кандидатом на тримминг и весь его «кеш» уйдет в пейджфайл.
Самые любознательные к этому моменту уже прочитали документ, на который я ссылался выше и знают правильное решение. Вот его реализация на шарпе:
using System; using System.Runtime.InteropServices; using System.IO; using System.Threading; using Microsoft.Win32.SafeHandles; namespace abuseio { class Program { [DllImport("kernel32.dll")] static extern IntPtr GetCurrentThread(); enum ThreadPriority { THREAD_MODE_BACKGROUND_BEGIN = 0x00010000, THREAD_MODE_BACKGROUND_END = 0x00020000, THREAD_PRIORITY_ABOVE_NORMAL = 1, THREAD_PRIORITY_BELOW_NORMAL = -1, THREAD_PRIORITY_HIGHEST = 2, THREAD_PRIORITY_IDLE = -15, THREAD_PRIORITY_LOWEST = -2, THREAD_PRIORITY_NORMAL = 0, THREAD_PRIORITY_TIME_CRITICAL = 15 } [DllImport("kernel32.dll")] static extern bool SetThreadPriority(IntPtr hThread, ThreadPriority nPriority); static void Main(string[] args) { for (var i = 0; i < 10; i++) { (new Thread(() => { SetThreadPriority(GetCurrentThread(), ThreadPriority.THREAD_MODE_BACKGROUND_BEGIN); var file = File.OpenRead(args[0]); var chunkSize = 16384; var chunks = (int)(file.Length / chunkSize); var rnd = new Random(); var buffer = new byte[chunkSize]; while (true) { long index = rnd.Next(chunks); file.Seek(index * chunkSize, SeekOrigin.Begin); file.Read(buffer, 0, chunkSize); } })).Start(); } } } }
Понижаются все, известные человечеству, приоритеты потока:
![](https://habrastorage.org/storage/6b694d8b/d7fee194/ec10e4ae/971c1890.png)
Несколько часов активной «раздачи» не приводят к вытеснению обычного кеша, но и от самого кеширования мы при этом не отказываемся:
![](https://habrastorage.org/storage/638c1112/96643167/bcb7da7a/ff21e542.png)
Все красиво сложено на самом низком уровне приоритета
![](https://habrastorage.org/storage/c3762dcc/2d263099/c9d95937/12ffebf7.png)
![](https://habrastorage.org/storage/03dace3c/3a76f2de/13ced6c1/2de1e3aa.png)
Теперь о том, как с жить с тем мюторрентом, который есть. Все проще, чем кажется и делается на 1-2-3.
1. Выключаем дурацкий кеш самого мюторрента и включаем кеш винды:
![](https://habrastorage.org/storage/8ab55e00/366c2831/a5a9b044/10d89aad.png)
2. Импортируем в реестр следующий файл:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utorrent.exe] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utorrent.exe\PerfOptions] "IoPriority"=dword:00000000 "PagePriority"=dword:00000001
3. Перезапускаем мюторрент
Результат:
![](https://habrastorage.org/storage/d316726b/28df91a8/6a2ab55a/75ee9304.png)
![](https://habrastorage.org/storage/ff8bfe86/37b37ce3/53d7a506/6dd2197d.png)
![](https://habrastorage.org/storage/86ea7466/f7c3654a/bb95695f/ca62537a.png)
Можно оставлять на ночь.
Пара слов о swappiness
Не знаю, я бы не назвал это «правильной работой с памятью». Вместо принятия решений на основе реального использования (aging/trimming, приоритеты в том числе динамические и пр) — какой то совершенно оторванный от жизни параметр. В общем то не только мне swappiness кажется не сильно хорошей идеей, сам автор low latency патча, реализовавшего swappiness говорит следующее:
Decrease /proc/sys/vm/swappiness?
Swapout is good. It frees up unused memory. I run my desktop machines at
swappiness=100.
И здесь я с ним совершенно согласен. Если приложение последний раз использовало страницу неделю назад, то можно достаточно безопасно выгрузить содержимое на диск, а освободившееся место использовать для кеширования чего нибудь полезного. Другое дело, что надо бы более интеллектуально подходить к поиску страниц, которые стоит выгрузить.
Разбирать весь флейм по ссылке ни желания ни сил у меня уже нет — читайте сами.
На этом пока все.