суббота, 24 июня 2017 г.

Использование Tor Expert Bundle на localhost

Использование Tor Expert Bundle не совсем тривиально, ввиду отсутствия внятной документации а также отсутствия в составе пакета некоторых необходимых компонентов.

Я хочу сразу оговориться, что речь идет о применении Tor не в оппозиционных и иных целях. Мне приходилось сталкиваться с ситуацией, когда в провайдерских сетях отсутствует внутрисетевой роутинг (по различным причинам), и в принципе необходимо попасть в соседнюю сеть (например, с целью администрирования собственных серверов), однако, ввиду повсеместного (и, зачастую, оправданного) резко отрицательного отношения к открытым прокси, это крайне затруднительно.

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

Итак, исходные данные. 

У нас имеется станция под управлением Windows и нам нужен сервис Tor, с точкой входа в виде SOCKS proxy на локальной машине для торификации некоторых программ с доступом в Интернет. Причем, по-возможности, не зависящий от произвола провайдеров и государственных служб (я предупреждал, господа).

Для решения данной задачи нам понадобится Tor Expert Bundle, а также Pluggable Transports, не входящие в состав пакета.

Вам, в ряде случаев, придется озаботиться тем, как попасть на https://torproject.org и получить оттуда два важных компонента: собственно Tor Expert Bundle и Tor Browser (он нужен, поскольку в его состав входят Pluggable Transports).

Сперва необходимо установить Тор браузер. По умолчанию он ставится в Desktop:


Обычно я просто собираю эту директорию в архив и сохраняю вместе с Tor Expert Bundle.

Здесь обычно задают вопрос - "А на кой мне возиться с Expert Bundle, если у меня уже есть работащий торифицированны браузер?" На той, что не только браузер может нуждаться в торифицированном соединении. А, например, SSH-клиент. Мессенджеры. И так далее. Да и, зачастую, удобнее иметь постоянно работающий сервис, нежели зависеть от браузера, который надо держать запущенным.

Сам Tor Expert Bundle нужно распаковать в Program Files, например в "C:\Program Files (x86)\tor" и туда же распаковать Pluggable Transports:



Самое сложное в использовании Expert Bundle - это конфигурирование. Сначала нужно создать сервис, это просто:


Обратите внимание на замечание. Сервис выполняется из-под локального системного аккаунта. Что означает, что конфигурационные файлы и данные находятся здесь (обратите внимание, torrc отсутствует и сервис запущен с умолчаниями):

Это не то, что нам нужно, к тому же, нас не удовлетворяет запуск сервиса от системного аккаунта (это достаточно небезопасно, к тому же, значительно сложнее сделать правильную конфигурацию). Поэтому запустим управление компьютером и поменяем аккаунт сервиса на пользовательский:

Окей, теперь у нас рабочая директория находится у пользователя. Если по указанному пути ее нет, создадим ее:

Теперь в нее можно положить torrc, который надо написать. Взяв за основу torrc браузера, напишем свой конфигурационный файл:

 ####  
 # cd C:\Users\Yuri\AppData\Roaming  
 # mkdir tor  
 SocksPort 9050 IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth  
   
 Bridge 198.147.22.36:443 58DB653508197599B8CB7EE95772F3EF2255939D  
 Bridge 194.132.209.138:22670 582E2CD0132AE1FD89426EC624B76B36690A622F  
 Bridge 194.132.209.116:20650 0D113A4B44E9B79C604DFC8DAE35C3C74CF60FF9  
   
 Bridge obfs4 194.132.209.138:48574 582E2CD0132AE1FD89426EC624B76B36690A622F cert=G1jt3rntMnoWTy8uEeQu9VPhnmPn7BH5cHwXbSkQDNlyrFD6BL7D8PMiPwPooKGudt1iYw iat-mode=0  
 Bridge obfs4 194.132.209.116:34596 0D113A4B44E9B79C604DFC8DAE35C3C74CF60FF9 cert=QfRegE3lzpqsnWUAQTulUPje7KgdcL2c+qOUtoU0Jw/ln8KpPZbh43XdfKr1kXYYLLcYQg iat-mode=0  
 Bridge obfs4 68.45.52.117:40365 3C89FB56CDEE23F0F16FDF86086866E33EAB24D8 cert=s0SmVQop+pZPZxlHunrXQL6MW4uVOZS55XjDVaBYkaSSoN9FEZOif/dxxrufg6ZnskRkSw iat-mode=0  
   
 Bridge obfs3 194.132.209.138:54673 582E2CD0132AE1FD89426EC624B76B36690A622F  
 Bridge obfs3 194.132.209.116:44364 0D113A4B44E9B79C604DFC8DAE35C3C74CF60FF9  
 Bridge obfs3 68.45.52.117:36125 3C89FB56CDEE23F0F16FDF86086866E33EAB24D8  
   
 GeoIPFile C:\Program Files (x86)\Tor\Data\Tor\geoip  
 GeoIPv6File C:\Program Files (x86)\Tor\Data\Tor\geoip6  
 HiddenServiceStatistics 0  
 UseBridges 1  
   
 ClientTransportPlugin obfs2,obfs3,obfs4,scramblesuit exec C:\Program Files (x86)\Tor\PluggableTransports\obfs4proxy  
   

Бриджи предварительно получим на https://bridges.torproject.org. Я обычно добавляю обычные бриджи для облегчения бутстрапа, но это может вызвать некоторые проблемы - обычные бриджи иногда банятся. Поэтому стоит добавить обфусцированные, запустив соответствующий транспортный плагин (внизу в конфигурации). Обратите внимание на полные пути (в кавычки не берем!). Чтобы не терять соединения в случае активного бана бриджей (а также ввиду того, что иногда они меняются или перестают работать) советую за несколько последующих дней собрать десяток-два бриджей в каждой группе. Обязательно укажите SOCKS-порт, как показано - он будет точкой входа, и, кстати, IPv6 будет полезен даже если в вашей стране IPv6 не применяется - изредка это облегчает бутстраппинг и последующую работу Тор. 

Если применению Тор активно противодействуют, откажитесь от использования необфусцированных бриджей.

Я не использую FTE, так как плагин достаточно капризный и в последней версии Tor Bundle не работает из-за ошибки (исправят - начну использовать снова). В принципе, OBFS3/4 вполне достаточно.

Убедитесь, что конфигурация работает, запустив Тор интерактивно (предварительно остановив сервис, разумеется) - что бутстраппинг проходит до конца и клиентское соединение устанавливается.

Убедившись, что все в порядке и Тор в состоянии соединиться с сетью, запустите сервис.

Обратите внимание вот на что. В Тор есть небольшой баг, касающийся остановки Pluggble Transport при остановке его самого. Ввиду этого может понадобиться руками убить выполнение obfs4proxy в диспетчере задач (и, в общем, это может понадобиться делать не раз при перезапусках):

Теперь, когда сервис работает, можно настроить, например, Телеграм на его использование:


Аналогично можно настроить, например, Mail.ru Agent (хотя я считаю глупостью небезопасный по определению мессенджер прятать в туннель), или SSH client:


Проверяем:

Порядок, соединяемся через Tor. Консоль даже не слишком тормозит.

Резюмируем. Вы имеете туннельный сервис с SOCKS5 на входе. Любой браузер, большинство мессенджеров - словом, все программы, могущие использовать SOCKS в соединениях - могут быть выпущены через туннель.

Пользуйтесь на здоровье. И не забывайте время от времени обновлять/пополнять бриджи в конфигурации, убирая умершие и накапливая рабочие. Опыт показывает, что, чем больше вы имеете бриджей в конфигурации - тем более бесперебойно работает сервис.

воскресенье, 30 апреля 2017 г.

С++: Fastest check all digits in std::string

По мотивам вот этой переписки самое быстрое из известных решение (С++11, никаких итераторов и других извращений):

 /* Fast check if string contains only digits */  
 static bool DigitsOnly(const std::string &p_str) {  
      for (size_t i = 0, v_len = p_str.length(); i < v_len; ++i) {  
            if ((p_str[i] ^ '0') > 9)  
                return false;  
      }  
      return true;  
 }  
   

Можете проделать бенчмарк, если есть сомнения.

суббота, 15 апреля 2017 г.

C++: Использование std::vector как dynamic array

Я вот здесь уже писал, как лучше организовывать пулы тредов переменной величины.

Так вот. Найдено наилучшее и наиболее элегантное решение. :)

      v_threads.resize(v_max_threads);  
      /*-------------Main Loop----------------*/  
      while (!std::cin.eof()) {  
           /* Start processing threads */  
           for (auto &t : v_threads) {  
                t = std::thread([]() { processdata(); });  
           }  
           /* Finish all threads */  
           for (auto &t : v_threads) {  
                t.join();  
           }  
      }  
      /*-------------Main Loop----------------*/  
   

Никаких push/emplace_back, никаких clear(). Вектор используется как array, причем с лучшими чертами динамических массивов и std::array.

Адресация элементов уже инициализированного вектора (resize вместо reserve) работает быстрее, чем push_back (примерно в четыре раза), и, хотя resize() и медленнее, чем reserve(), но она находится вне цикла и выполняется только один раз.

пятница, 14 апреля 2017 г.

Solaris: диагностика lock contention


Все знают одну из самых известных проблем с производительностью, а, точнее, с масштабированием. Это конкуренция блокировок (lock contention).

На первый взгляд кажется, что многопоточные приложения должны масштабироваться линейно. Так считают зачастую даже весьма опытные администраторы и даже (!) разработчики.

Как бы не так.

Невозможно всегда и везде писать lock-free код. Атомарные операции предельно ограничены областями, в которых их вообще можно использовать.

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

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

Вместе с тем, стоит отметить, что от среды разработки этот эффект, в общем, зависим мало - можно и с использованием GCC/STL понаписать такого, что куча процессорных ядер будет стоять колом в ожидании снятия блокировки.

Впрочем, речь не об этом.

Поговорим о том, как можно диагностировать lock contention, так как возникновение конкуренции блокировок вообще неочевидно и не всегда легко обнаруживается. 

Это важно, так как чтобы устранить bottleneck, надо его сперва обнаружить.

lockstat и standalone diagnostics

Если у вас есть возможность выделить самостоятельную программу для автономного запуска, вы можете использовать утилиту lockstat для диагностики конкуренции блокировок в различных режимах выполнения вашего кода (ну, или не вашего - главное, иметь возможность его запуска).

Я использовал в примерах одну программу, написанную в соавторстве с Joe Lawand, которая выполняется в асинхронном режиме (тредами) в диапазоне от 1 до 65534 треда на процесс.

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

 std::mutex g_cin_mtx, g_cout_mtx, g_log_mtx;     /* cin/cout/log guards */  

Как я и сказал, мьютексы ограничены очень короткой областью видимости:

      {  
           std::lock_guard<std::mutex> lock(g_cout_mtx);  
           std::cout << v_out;  
      }  
   

и снимаются немедленно по завершении критической секции кода.

Для тестов использовался 8-ядерный сервер с двумя процессорами Xeon под Solaris 10:

 root @ khorne / # psrinfo    
 0    on-line  since 04/12/2017 13:18:20  
 1    on-line  since 04/12/2017 13:18:31  
 2    on-line  since 04/12/2017 13:18:31  
 3    on-line  since 04/12/2017 13:18:31  
 4    on-line  since 04/12/2017 13:18:31  
 5    on-line  since 04/12/2017 13:18:31  
 6    on-line  since 04/12/2017 13:18:31  
 7    on-line  since 04/12/2017 13:18:31  
 root @ khorne / # psrinfo -v  
 Status of virtual processor 0 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:20.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
 Status of virtual processor 1 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:31.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
 Status of virtual processor 2 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:31.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
 Status of virtual processor 3 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:31.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
 Status of virtual processor 4 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:31.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
 Status of virtual processor 5 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:31.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
 Status of virtual processor 6 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:31.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
 Status of virtual processor 7 as of: 04/14/2017 19:40:41  
  on-line since 04/12/2017 13:18:31.  
  The i386 processor operates at 2333 MHz,  
     and has an i387 compatible floating point processor.  
   

Значение степени параллелизма по умолчанию у программы равно 4 треда на процесс, и в нормальном состоянии конкуренции блокировок не фиксируется:

 root @ khorne / # lockstat -s 10 -I ./store-id-helper      
  
 Profiling interrupt: 16 events in 0.021 seconds (750 events/sec)  
   
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   2 12% 12% 0.00   1280 cpu[0]         i86_mwait+0xd        
   
    nsec ------ Time Distribution ------ count   Stack            
    2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2     cpu_idle_mwait+0x145    
                            idle+0x89          
                            thread_start+0x8      
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   2 12% 25% 0.00   1899 cpu[7]         i86_mwait+0xd        
   
    nsec ------ Time Distribution ------ count   Stack            
    2048 |@@@@@@@@@@@@@@@        1     cpu_idle_mwait+0x145    
    4096 |@@@@@@@@@@@@@@@        1     idle+0x89          
                            thread_start+0x8      
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   2 12% 38% 0.00   1392 cpu[6]         i86_mwait+0xd        
   
    nsec ------ Time Distribution ------ count   Stack            
    2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2     cpu_idle_mwait+0x145    
                            idle+0x89          
                            thread_start+0x8      
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   2 12% 50% 0.00   1467 cpu[5]         i86_mwait+0xd        
   
    nsec ------ Time Distribution ------ count   Stack            
    2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2     cpu_idle_mwait+0x145    
                            idle+0x89          
                            thread_start+0x8      
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   2 12% 62% 0.00   4039 cpu[3]         i86_mwait+0xd        
   
    nsec ------ Time Distribution ------ count   Stack            
    2048 |@@@@@@@@@@@@@@@        1     cpu_idle_mwait+0x145    
    4096 |                0     idle+0x89          
    8192 |@@@@@@@@@@@@@@@        1     thread_start+0x8      
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   2 12% 75% 0.00   1238 cpu[1]         i86_mwait+0xd        
   
    nsec ------ Time Distribution ------ count   Stack            
    2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2     cpu_idle_mwait+0x145    
                            idle+0x89          
                            thread_start+0x8      
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   2 12% 88% 0.00   949 cpu[2]         i86_mwait+0xd        
   
    nsec ------ Time Distribution ------ count   Stack            
    1024 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2     cpu_idle_mwait+0x145    
                            idle+0x89          
                            thread_start+0x8      
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   1  6% 94% 0.00   1236 cpu[4]         anon_get_slot+0x82     
   
    nsec ------ Time Distribution ------ count   Stack            
    2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1     anon_array_enter+0x105   
                            segvn_faultpage+0x124    
                            segvn_fault+0x98f      
                            as_fault+0x205       
                            pagefault+0x8b       
                            trap+0x3d7         
                            cmntrap+0x140        
 -------------------------------------------------------------------------------  
 Count indv cuml rcnt   nsec CPU+PIL        Caller           
   1  6% 100% 0.00   2581 cpu[4]         mutex_enter+0x10      
   
    nsec ------ Time Distribution ------ count   Stack            
    4096 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1     hment_prepare+0x6b     
                            hati_pte_map+0x1e3     
                            hati_load_common+0x12b   
                            hat_memload+0x6a      
                            hat_memload_region+0x9   
                            segvn_faultpage+0x264    
                            segvn_fault+0x98f      
                            as_fault+0x205       
                            pagefault+0x8b       
 -------------------------------------------------------------------------------  

Как видно, ожидания снятия блокировок есть, но очень короткие.

Запустим теперь 1000 тредов на один процесс:

 lockstat -s 10 -I ./store-id-helper<t1.txt -1000  

Взгляните, появились ожидания LWP:


Вот именно то, о чем я говорил - конкуренция за блокировки. Потоки стоят в очереди, ожидая освобождения двух мьютексов.

Более наглядно это можно увидеть на гистограмме:


Значения счетчиков события ожидания выросли. Становится видно, что ожидающий процесс переходит в состояние idle и планировщик снимает его с выполнения.

plockstat и runtime diagnostics


Предыдущий пример показывает, как использовать standalone diagnostics. Чаще всего, однако, нам необходима диагностика на живых системах, в режиме runtine execution.

Для этого используем plockstat.

Данная утилита позволяет увидеть lock contention еще более наглядно. Чтобы довести ситуацию до абсурда, выполним два прогона: один в дефолтной конфигурации (4 треда), и один - с запуском максимального возможного количества, по верхней границе лимита программы:

 /* No lock contention - 4 threads */  
   
 root @ khorne / # plockstat -C -n 1000 -s 1 -e 1 -p 4295   
     0  
 root @ khorne / #   
   
   
   
 /* Lock contention - 65534 threads */  
   
   root 17765 16422  2 18:46:14 pts/2    0:05 store-id-helper -65534  
 root @ khorne / # plockstat -C -n 1000 -s 1 -e 1 -p 17765  
     0  
 Mutex block  
   
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   22 204393378 libc.so.1`__sbrk_lock    0  
   
    nsec ---- Time Distribution --- count Stack  
    8192 |@@           |   2 0  
    16384 |@            |   1   
    32768 |            |   0   
    65536 |@            |   1   
   131072 |@@           |   2   
   262144 |            |   0   
   524288 |            |   0   
   1048576 |            |   0   
   2097152 |            |   0   
   4194304 |            |   0   
   8388608 |            |   0   
  16777216 |            |   0   
  33554432 |            |   0   
  67108864 |@@@@@@@@@        |   9   
  134217728 |@            |   1   
  268435456 |@@           |   2   
  536870912 |@@           |   2   
 1073741824 |@@           |   2   
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   3 447414272 0x449740           0  
   
    nsec ---- Time Distribution --- count Stack  
    65536 |@@@@@@@@        |   1 0  
   131072 |            |   0   
   262144 |            |   0   
   524288 |            |   0   
   1048576 |            |   0   
   2097152 |            |   0   
   4194304 |            |   0   
   8388608 |            |   0   
  16777216 |            |   0   
  33554432 |            |   0   
  67108864 |            |   0   
  134217728 |            |   0   
  268435456 |@@@@@@@@        |   1   
  536870912 |            |   0   
 1073741824 |@@@@@@@@        |   1   
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   3 268457301 0x449980           0  
   
    nsec ---- Time Distribution --- count Stack  
    65536 |@@@@@@@@        |   1 0  
   131072 |            |   0   
   262144 |            |   0   
   524288 |            |   0   
   1048576 |            |   0   
   2097152 |            |   0   
   4194304 |            |   0   
   8388608 |            |   0   
  16777216 |            |   0   
  33554432 |            |   0   
  67108864 |            |   0   
  134217728 |            |   0   
  268435456 |@@@@@@@@        |   1   
  536870912 |@@@@@@@@        |   1   
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   13 41341873 libc.so.1`_uberdata     0  
   
    nsec ---- Time Distribution --- count Stack  
    8192 |@@@           |   2 0  
    16384 |            |   0   
    32768 |@            |   1   
    65536 |@@@@@@@         |   4   
   131072 |@@@           |   2   
   262144 |            |   0   
   524288 |            |   0   
   1048576 |            |   0   
   2097152 |            |   0   
   4194304 |            |   0   
   8388608 |            |   0   
  16777216 |            |   0   
  33554432 |            |   0   
  67108864 |@@@           |   2   
  134217728 |@            |   1   
  268435456 |@            |   1   
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   10 47026176 store-id-helper`g_cout_mtx  0  
   
    nsec ---- Time Distribution --- count Stack  
    8192 |@@           |   1 0  
    16384 |            |   0   
    32768 |@@           |   1   
    65536 |@@           |   1   
   131072 |@@           |   1   
   262144 |@@           |   1   
   524288 |            |   0   
   1048576 |            |   0   
   2097152 |            |   0   
   4194304 |            |   0   
   8388608 |            |   0   
  16777216 |            |   0   
  33554432 |            |   0   
  67108864 |@@@@@@@         |   3   
  134217728 |@@@@          |   2   
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   1 268435456 0x4497c0           0  
   
    nsec ---- Time Distribution --- count Stack  
  268435456 |@@@@@@@@@@@@@@@@@@@@@@@@|   1 0  
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   1 268435456 0x449700           0  
   
    nsec ---- Time Distribution --- count Stack  
  268435456 |@@@@@@@@@@@@@@@@@@@@@@@@|   1 0  
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   1 134217728 0x449880           0  
   
    nsec ---- Time Distribution --- count Stack  
  134217728 |@@@@@@@@@@@@@@@@@@@@@@@@|   1 0  
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   1  262144 0x449840           0  
   
    nsec ---- Time Distribution --- count Stack  
   262144 |@@@@@@@@@@@@@@@@@@@@@@@@|   1 0  
 -------------------------------------------------------------------------------  
 Count   nsec Lock             Caller  
   1  262144 0x449800           0  
   
    nsec ---- Time Distribution --- count Stack  
   262144 |@@@@@@@@@@@@@@@@@@@@@@@@|   1 0  
 root @ khorne / #   
   

Тут все совершенно очевидно. Нет конкуренции - нет вывода, выводится ноль и все. Во втором случае у нас в ожидании находится свыше 60к процессов, мы видим, что ожидания вызывает именно мьютекс  g_cout_mtx.

Как видите, все просто. Если, при этом, вы видите исходный код, то определение проблемных мест сразу позволяет перейти к их оптимизации.

PS. Интересный факт. Древний (и давно устаревший) top показывает максимум 999 LWP:


Впрочем, в Солярис он отсутствует (его устанавливают отдельно - а потом ему всецело верят, что зря).

А вот штатный prstat показывает все честно, как есть (и в нем нет странных лимитов):



среда, 29 марта 2017 г.

Оптимизация Squid 3+: Часть IV

Конфигурация


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

Характерные примеры подобных ошибок:
  • Слепое копирование default-конфигурации, слепое копирование чужой конфигурации
  • Наполнение конфигурации дефолтными или тупо задранными до максимума значениями
  • Несбалансированные конфигурации, написанные бездумно либо с непропорциональными параметрами, впрямую относящимися к производительности либо ресурсам
  • Неэффективные, чрезмерно избыточные конфигурации с гигантскими ACL либо с избыточно дискретными ACL
  • Несуразные конфигурации - например, чрезмерно гигантский memory cache при cache_dirs по умолчанию или при его отсутствии
Основная причина подобных ошибок - игнорирование концептуальной архитектуры, документации, и непонимание основных принципов работы кэша.

Правила пальца №1. Вдумчиво изучите squid.conf.documented. Пишите свой конфиг с нуля. Структурируйте его. Порядок - важен. Не пишите в него значений по умолчанию, исключая те, которые нужны или полезны для понимания смысла конфигурации.

Параметры кэширования

cache_mem - не задавайте данный параметр равным половине физической памяти или большей ее части. Законы сохранения действуют - где, вы думаете, должна работать ОС и остальные сервисы? Помимо того, что чрезмерно большой кэш может просто быстро привести к панике ядра, он может оказаться медленнее дисковой системы. Кроме того, не забывайте о необходимости памяти для остальных задач кэша, включая обработку запросов, парсинг, кэширование метаданных - все это тоже размещается не в космосе. А кэш метаданных еще и непрерывно прирастает по мере заполнения дискового кэша. 1/8 - 1/4 RAM будет вполне достаточно.

memory_pools - не все ОС толерантно относятся к пулам, иногда пулы текут, иногда они превышают лимиты, и они весьма редко освобождаются. Следует использовать пулы только в случаях, когда они действительно дают выигрыш и помнить правило - "Памяти много не бывает - вранье".

memory_replacement_policy heap GDSF
cache_replacement_policy heap LFUDA

Два этих параметра намеренно привожу в оптимальных (в большинстве случаев) значениях. LRU древний и не слишком эффективный алгоритм замещения, так как он игнорирует распределение объектов по размерам. heap-политики почти во всех случаях оказываются более эффективными - раз, и два - в кэше памяти следует размещать по-возможности часто запрашиваемые некрупные объекты, а на дисках - в первую очередь крупные объекты. Рекомендую почитать об алгоритмах GDSF и LFUDA для того, чтобы определить, подходят ли они вам.

maximum_object_size_in_memory 1 MB - ограничивайте размер объектов в кэше памяти. Но и не занижайте чрезмерно эту величину, things changes.

minimum_object_size 10 bytes
maximum_object_size 4 Gb

Мне не кажется хорошей идея набивать кэш веб-багами размером 1 байт (каждый из которых занимает метаданные целого файла), точно так же, как тратить ресурсы на их кэширование. С другой стороны, не стоит хранить редко используемые гигантские объекты - как правило это неразумно.

cache_dir должен соответствовать (хотя бы приблизительно) объему, который в нем хранится. То есть глупо использовать дефолтные параметры иерархии и задавать размер 1 Тб. Пример относительно эффективных параметров cache_dir (в вашем случае может оказаться неэффективным или неработоспособным!):


 cache_dir diskd /data/cache/d1 48000 64 512  
 cache_dir diskd /data/cache/d2 48000 64 512  
 cache_dir diskd /data/cache/d3 48000 64 512  
 cache_dir diskd /data/cache/d4 48000 64 512     

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

SSD могут решить проблему узких мест в СХД, если у вас много денег и есть запасные на замену - интенсивность записи при прогреве и replacement дисковых кэшей весьма высокая. Ресурс может оказаться ограничен. Однако выигрыш в скорости может стоить таких затрат.

При избытке оперативной памяти рассмотрите возможность применения RAM disks для наиболее узких мест - например, кэша сертификатов ssl_crtd (предусмотрите сброс его содержимого на жесткие диски при остановке сервисов кэша). Это более целесообразно, нежели выделение гигантского cache_mem.

Списки контроля доступа

Как и в случае firewall, следует избегать написания гигантских ACL в сотни и тысячи элементов. Помимо того, что они занимают много места в памяти и сильно замедляют перезапуск и refresh, они чрезвычайно замедляют обработку запросов (помните? Однопоточное ПО. Обработка этих списков идет последовательно).

Хинт: ACLы на основе регулярных выражений поддаются одной хитрой и неочевидной оптимизации. Группы с вариантами на основе OR (|) работают быстрее, чем последовательный список всех вариантов. То есть, вот эта строка:

 (stnd\-avpg|avs\-avpg|stnd\-ipsg|bash\-avpg)\.crsi\.symantec\.com  

обрабатывается в несколько раз быстрее, чем ее последовательный эквивалент:

 stnd\-avpg\.crsi\.symantec\.com  
 avs\-avpg\.crsi\.symantec\.com  
 stnd\-ipsg\.crsi\.symantec\.com  
 bash\-avpg\.crsi\.symantec\.com\.com  

Хитрость здесь в том, что последовательное применение функции regex_match требует больше времени, имеет больший оверхед и займет более, чем вдвое больше времени, чем вариант, приведенный выше. Как ни странно, выражения OR обрабатываются в одном вызове regex_match более эффективно. 

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

refresh_pattern

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

 # Updates: Windows  
 refresh_pattern (windowsupdate|microsoft|windows)\.com/.*\.(cab|exe|ms[i|u|f|p]|[ap]sf|wm[v|a]|dat|zip|psf) 43200 80% 129600 reload-into-ims  
 refresh_pattern microsoft\.com\.akadns\.net/.*\.(cab|exe|ms[i|u|f|p]|[ap]sf|wm[v|a]|dat|zip|psf) 43200 80% 129600 reload-into-ims  
 refresh_pattern deploy\.akamaitechnologies\.com/.*\.(cab|exe|ms[i|u|f|p]|[ap]sf|wm[v|a]|dat|zip|psf) 43200 80% 129600 reload-into-ims     

Правило пальца: строк refresh_pattern должно быть как можно меньше. Избегайте применения опции -i без необходимости.

DNS

Быстрый DNS имеет прямое влияние на производительность прокси. Как ни странно, статистика показывает, что, почти во всех случаях, не имеет смысла сильно увеличивать параметр ipcache_size. Значения 4096 достаточно в обычных (не слишком крупных) конфигурациях.

Гораздо лучше разместить высокоскоростной рекурсор (например, Unbound) прямо на прокси-боксе либо не далее одного хопа от него. И нацелить кэш непосредственно на него. Только не увлекайтесь чрезмерными TTL для кэшированных ответов на рекурсоре - может привести к проблемам с CDN (хотя да, значительно улучшает отклик). Защищайте рекурсор - это вектор для атаки.

Разное

Есть пара параметров, которые могут отказаться полезными в оптимизации дискового кэша:

 # Default is 20  
 store_objects_per_bucket 32  
   
 store_avg_object_size 200 KB  

Хочу, однако, обратить внимание на то, что данные параметры задаются на основе анализа статистики кэша, а не высасываются из пальца. store_avg_object_size, например, задается на основании параметра Mean object size из статистики (обычно ставится численно равным или близким к статистическому показателю). store_objects_per_bucket влияет на структуру swap-файлов, хранящих хэши и может варьироваться для уменьшения оверхеда. Однако не меняется без перестроения swap-файлов (переиндексации кэша) и изменения не заметны немедленно.

Заключение: Волшебной пули не существует

Оптимизация - это кропотливый процесс, связанный с длительным сбором статистики, ее анализом, и подбором необходимых (не всех и не наугад) параметров. Не увлекайтесь. Синдром чрезмерного администрирования - это профессиональное заболевание. :)