Каталог:
Один
Два
Три
μTorrent
Это, пожалуй, самая забавная часть. При очевидно высоких навыках программирования, авторы либо не читали, либо не поняли вот этот документ. Вот что происходит с дефолтными настройками:
Поток 4600 открывает для раздачи файл с отключенным системным кешем. Причем все выполняется без снижения приоритета:
Я не пользуюсь торрентами и файлов для раздачи у меня не оказалось, да и каналы на отдачу в местных широтах довольно узкие, так что мне пришлось написать «эмулятор» мюторрента. Для начала «старая версия», не выключавшая кеширование (написал на сишарпе, потому что не у всех может оказаться установленная 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(); } } } }
В несколько потоков читаем случайные места предоставленного файла (лучше использовать что нибудь большое). Несколько минут после старта:
Видим довольно много кешированных файлов. Несколько часов после старта:
В памяти остался только хром (в основном потому, что я им активно пользовался) и ругие Active страницы файлов, означающие недавнее использование. В Standby остались в основном те самые priority 7 страницы:
Вот, к примеру, небольшая часть shell32.dll:
Ну и «общий вид»:
Почти все закешировалось на пятый уровень, вытеснив всех, кто находился там до этого. Более того, из-за частых обращений к «раздаваемым» файлам, суперфетч динамически повысил приоритет частей этих файлов и вытеснил часть кеша даже оттуда. Короче, все плохо.
Вот «эмулятор» нового мюторрента:
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(); } } } }
Понижаются все, известные человечеству, приоритеты потока:
Несколько часов активной «раздачи» не приводят к вытеснению обычного кеша, но и от самого кеширования мы при этом не отказываемся:
Все красиво сложено на самом низком уровне приоритета
Теперь о том, как с жить с тем мюторрентом, который есть. Все проще, чем кажется и делается на 1-2-3.
1. Выключаем дурацкий кеш самого мюторрента и включаем кеш винды:
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. Перезапускаем мюторрент
Результат:
Можно оставлять на ночь.
Пара слов о 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.
И здесь я с ним совершенно согласен. Если приложение последний раз использовало страницу неделю назад, то можно достаточно безопасно выгрузить содержимое на диск, а освободившееся место использовать для кеширования чего нибудь полезного. Другое дело, что надо бы более интеллектуально подходить к поиску страниц, которые стоит выгрузить.
Разбирать весь флейм по ссылке ни желания ни сил у меня уже нет — читайте сами.
На этом пока все.