Показаны сообщения с ярлыком Delphi. Показать все сообщения
Показаны сообщения с ярлыком Delphi. Показать все сообщения

09.07.2023

Кирпичи Эйлера. Вторая итерация

Иван в комментариях к предыдущему посту придумал очень быстрый и эффективный способ вычисления пифагоровых троек к нечетному числу. Мне стало интересно, я тоже немного погрузился в тему статьи Матсона.

Правда, той эффективности, что у Ивана, мне достичь не удалось, я пока в процессе. И в этом процессе я обнаружил любопытную вещь.
Мне скучно просто искать совершенный кубоид, поэтому  я решил в процессе еще и сохранять все найденные обычные кирпичи Эйлера. Это чуть более сложная задача, потому должна работать чуть медленнее, чем поиск совершенного кубоида. И для её работы нужно уметь эффективно считать целочисленный корень для больших целых чисел, в моем случае 128-битных.
Я решил проверить, сможет ли FPU (или по-другому x87) выполнить эту задачу. Шансы у него были, так как во внутреннем представлении FPU хранит вещественные в расширенном формате, длиной не стандартные 64, а целых 80 бит.
В процессе обнаружил, что точности как бы не хватает. Начал копать и через непродолжительное время выяснил, что по какой-то странной причине точность FPU оказалась занижена до 64 бит. Да, в FPU с помощью флагов можно менять точность внутреннего представления чисел от 32 до 80 бит, что кроме точности влияет и на производительность, понятное дело.
В документации на сайте AMD утверждается, что по умолчанию FPU установлен на максимальную точность. Но возможно, это было давно и в современных версиях процессора уже не действует? Кроме того, точность может менять Windows, а так же компилятор Delphi. Кто из них повинен в такой ситуации, мне не ведомо.
Интереснее другое: зачем это вообще сделано? Лично я вижу лишь одну причину: что бы пользователи не заметили разницу в решениях своих задач на FPU и SSE, иначе кто-нибудь как-нибудь да непременно выскажет претензии. Но это лишь предположение.

Что касается целочисленного корня. При максимальной точности FPU может находить точное целое значения корня из 128-битового беззнакового целого. Но вот точности, что бы определить, целым ли получился результат, уже не хватает, а было бы очень кстати.

Теперь про мою реализацию. Во-первых, я отказался от проверки деления на 19. Конечно, такое деление снижает количество рассматриваемых вариантов, но, если я правильно понял, не так уж значительно по сравнению с делением на 16. В то же время, проверка делимости на 16 происходит гораздо быстрее.
Но возможно здесь я ошибаюсь, так как проверка каждого варианта на то, что он является кирпичом Эйлера стоит гораздо дороже, чем разница в скорости проверок делимости.

Во-вторых, я пока не сделал сохранение найденных делителей для последующего использования, что, конечно, существенно снижает производительность.

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

Все это, конечно, весьма далеко от  гораздо более эффективной реализации Ивана. Но, тем не менее, тоже показывает относительно неплохую производительность: от 100001 до 100000001 по нечетной стороне все варианты кирпичей Эйлера были найдены за час с небольшим, всего 3182002 варианта.
Я решил, что вместо того, что бы проверять на первичность решения в процессе поиска (мне показалось это достаточно затратной операцией), сохранять все найденные варианты. А уже потом быстренько отсеять все составные, ну и плюс тут же проверить и на совершенность.

29.06.2023

Кирпичи Эйлера

Как-то раз случайно наткнулся на видео, не помню уж про что оно было, но упомянуто в нем было в том числе и про одну из нерешенных математических проблем. Как раз про совершенный кирпич Эйлера.

Кратко: суть состоит в решение системы четырех диофантовых уравнений второй степени. Это решение соответствует параллелепипеду, у которого все стороны и диагонали являются натуральными числами.

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

Естественно, есть соблазн найти такой кирпич с помощью грубой силы. А раз есть соблазн, значит, есть и те, кто попытается им воспользоваться.
К сожалению, на текущий момент найти совершенный кирпич Эйлера таким методом тоже не удалось. И даже дойти до границы 64-битных чисел для длин сторон кирпича тоже.
Что, если честно, меня несколько удивило. Ведь производительность современных компьютеров просто край как высока. Несколько десятков миллиардов целочисленных операций в секунду.

Естественно, я не удержался и решил сам попробовать, не мудрствуя лукаво, грубой силой в лоб. Ну и в общем да, получил то, что и следовало ожидать: два вложенных цикла – это совсем не быстро, даже с учетом того, что память при таких вычислениях мало задействована.
Оптимизация на ассемблере существенно ускорила вычисления, но, конечно, кардинально решить проблему не смогла: слишком высока размерность задачи.

Больше всего издержек при такой попытке поиска кирпичей Эйлера уходит на проверку того, будет ли целым корень из квадрата длины диагонали.
И тут, по крайней мере, пока квадрат длины не выходит за границы 64-битого беззнакового целого, очень помогают вычисления с плавающей точкой.
Сначала я сделал это на расширенном типе с плавающей точкой (extended), являющейся фишкой x87, так как 64-битное беззнаковое целое можно без какой-либо потери точности загрузить в регистр сопроцессора. Еще подумал: "О, вот и старенький x87 может для чего-нибудь пригодиться".
Однако потом в процессе тестов выяснил, что с достаточной степенью точности для данной задачи корни можно вычислять и на типе double. Впрочем, особой разницы в производительности не увидел.

Но, как только мы выйдем за границы 64 бит для квадратов длин диагоналей, вот возможно там тип extended, вместе с x87 и сгодится.
Целочисленные методы вычисления квадратного корня на основе метода Ньютона (он же вавилонский, он же Герона), даже при хорошем начальном приближении требуют минимум двух итераций с делением, что получается гораздо медленнее, чем с плавающей точкой. Главное, что бы хватало точности у этой самой плавающей точки. И вот тут я не уверен, что ближе к верхней границе 128-битного целого точности даже extended будет достаточно.

В практическом же смысле толку от этих самых кирпичей Эйлера я не вижу. Разве что в холода использовать комп в качестве нагревателя. )
И вот идея: так как кирпичи Эйлера трудно вычислимы, почему бы их не использовать в качестве криптовалюты? Тот же специальный вид криптографического хеша тоже особого смысла не имеет. А на майнинг опять же биткойнов потрачено, наверное, уже несколько тераватт*часов электроэнергии.
При этом хоть какую-то ценность все эти криптовалюты имеют до тех пор, пока у достаточно значительной части населения есть вера в крипту хотя бы с горчичное зерно. Как только такая вера пропадет, стоимость крипты мгновенно обнулится.

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

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

Ну и немного статистики. Выяснилось, что примерно 80% длин дают только один вариант кирпича, а вот оставшиеся 20% – несколько. Интересно, что это за числа и чем вызвано такое их свойство? Не знаю, есть ли у математиков ответ на такой вопрос.
Лидером среди нечетных длин является число 51975
оно дает 12 различных кирпичей, или 24 пифагоровых тройки. Далее идут 32175 и 58905 по 11 кирпичей.
Четные числа немного отстают. Лидер тут 23760 
– 11 кирпичей, 71280 9, и у 55440 и 118800 по 8. Причем такое свойство гораздо чаще встречается у стороны, кратной 16, чем у стороны, кратной лишь только четырем.
Лидер, кратный 4 - 33660, дает 7 кирпичей.

Из найденных вариантов самый близкий к совершенному варианту оказался кирпич с длинами сторон 37835, 269280 и 1244484. Главная диагональ у него 1273846.00006476, что совсем близко к целому числу.
Это дает надежду, что совершенный кирпич Эйлера все же существует. Но скорее всего длины сторон лежат за пределами 64-битных целых, так как значительная часть этого диапазона уже проверена исследователями.
А вот в диапазоне 128-битных целых такой кирпич вполне может прятаться.

19.10.2022

Возвращаясь к копированию файлов

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

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

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


 
 


где D – объем копируемых данных, Vr
– скорость чтения, Vw – скорость записи данных на соответствующих дисковых устройствах.

При параллельном копировании с использованием многопоточной архитектуры время копирования составит



 


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

Но это происходит ровно до того момента, пока в работу не вступает системный файловый кэш на уровне ОС. Такой кэш реализован в виде отдельного системного процесса и работает всегда независимо и параллельно пользовательскому процессу.
Фактически это приводит к тому, что последовательное копирование при наличии файлового кэша по факту превращается в параллельное и примерно соответствует ему по скорости.
Но всегда ли?

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

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

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

Таким образом, один из двух случай, когда параллельное копирование может быть существенно быстрее, чем последовательное с использованием кэширования, возникает тогда, когда объем копируемых данных существенно превышает объем кэша и копирование производится с более быстрого устройства на более медленное. Точнее, объем копируемых данных должен превышать половину кэша, так как из всего доступного объема половина будет использоваться под чтение.
Впрочем, это ограничение на половину объема можно снизить, правильно указав флаги использования копируемых файлов.
Если отбросить простые алгебраические манипуляции, то в этом случае время копирования файлов составит



 

 

где B – размер буфера записи, k – коэффициент > 1, показывающий, во сколько раз скорость чтения больше скорости записи, то есть Vr = k*Vw.

Отсюда легко получаем ускорение параллельного копирования по отношения к последовательному с кэшем



 

 

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

Но насколько велика такая задержка? Приблизительно её можно оценить так



 


где С
– размер блока read-ahead в системе файлового кэширования.
Насколько же она велика? Это зависит от размера блока и может сильно отличаться в разных версиях и вариантах системы кэширования. В простейших случаях она имеет фиксированный размер и, например, для какой-то (уже не помню номер) версии Microsoft Windows Server составляет 256 КиБ.
В любом случае можно констатировать, что она не очень велика, что подтверждается и моим тестами. Например, когда скорость дисков источника и назначения сравнима, то выигрыш параллельного копирования очень невелик и примерно соответствует приведенной величине задержке, умноженной на количество копируемых файлов за вычетом единицы.
Поэтому в том случае, когда мы копируем небольшое количество значительных по размеру файлов, такую задержку можно не учитывать, как и было в расчета времени копирования выше.

Но только не тогда, когда мы копируем очень много мелких файлов. И это второй случай, когда параллельное копирование будет значительно опережать последовательное с кэшем.

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

Ну а пока, в текущей версии, сделал немного более информативный интерфейс, а то он был совсем уже слепой, да пару косячков исправил.

Кстати, для копирования файлов в параллельном режиме без использования кэширования вполне достаточно очень небольшого буфера. Его размер примерно можно определить по скорости работы накопителей. Точнее это можно сделать по IOPS, но сути это не меняет.
Сейчас я, например, исхожу из того, что размер буфера должен быть равен сумме объемов данных всех устройств, задействованных в копировании, которые они могут передать за один цикл переключения потоков. В Windows он скорее всего равен 55 мс, но может быть и меньше.
Этого хватает с большим запасом для поддержания стабильной скорости, при этом объём памяти, занимаемой программой, не превышает 25 МиБ. Потенциально может иметь важное значение в тяжело нагруженных системах, интенсивно работающих с ОЗУ.

13.10.2022

Автотрассировка 2

Немного подправил программку, был там небольшой глючок: здесь 32-битна версия, а здесь – 64-битная. Тем не менее она все также в основном предназначена только для наблюдения.

Выглядит всё это безобразие вот так:

Внизу график задержек пинга (для трех разных хостов) в зависимости от времени. Видно, что характер пинга меняется: около 10 часов я запустил скачивание торрентов, которое сразу после 12 закончилось.

А вот в 11:40 у меня фактически отвалился интернет, примерно на 12 минут. К сожалению, та проблема, с которой всё и началось, так и осталась, хотя и возникает после замены домового коммутатора провайдером гораздо реже. Видимо, решить проблему с этим провайдером в настоящий момент не представляется возможным: проблема плавающая, возникает не каждый день и в разное время.
Такое ощущение, что какой-то процесс флудит домовой коммутатор, например, широковещательным пакетами, или, возможно, где-то происходит рассогласование протокола DHCP. Потому что в момент потери связи сильно увеличивается время отклика даже моего домашнего роутера, то есть какие-то пакеты снаружи перегружают его процессор.
К сожалению, маршрутизаторы TP-Link серии Archer может и неплохи по характеристикам, но совершенно лишены интерфейса для наблюдения за сетью. Ну или я просто не знаю, как правильно их готовить 😅. Вроде даже у D-Link
  было несколько лучше.
Впрочем, даже наличие каких-то продвинутых средств администрирования вряд ли помогло бы мне диагностировать проблему, так как в момент сбоя процессор коммутатора настолько загружен, что подключить к нему через Web-интерфейс невозможно.

Видимо, придется со временем все же поменять провайдера. У нас в Сибири это стандартная практика – когда провайдер не может оперативно решить проблему, то его нужно менять на другого. Через некоторое, достаточное большое время, у второго тоже гарантированно возникают аналогичные проблемы и тогда просто возвращаешься к первому, который через несколько лет все же уже навёл порядок в этой части своей сети.
Я уже пару раз так делал в Новокузнецке. 😀

04.10.2022

Автотрассировка

Сделал простенькую программку, которая отдаленно напоминает PingPlotter. Пока что никаких полезных действий она делать не умеет, кроме наблюдения за состоянием сети с ограниченным набором тестируемых хостов. Интерфейсик простенький, корявенький и наверняка имеет какие-то глюки. Тем не менее, наблюдать она уже могёт.

Если вдруг кому нужно помониторить сеть, то программка лежит здесь. Ее можно просто положить в какое-нибудь удобное место на локальном комьютере и оттуда запускать.

Реализовал я ее в многопоточном режиме, программа создает кучу потоков, которые просто заняты тем, что параллельно отправляют ICMP пакеты и ждут ответа.
Наверное, не самый оптимальный вариант, было бы лучше это сделать в асинхронном режиме и такой вариант ICMP API у Microsoft есть, но мне пока лень разбираться, потому что сделан он не очень-то прозрачным.

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

Вот фрагмент кода, содержащего ошибку:

try
  ...
  GetMem(pIpe,...);
  ...
  IcmpSendEcho(...);
  error := GetLastError();
  if (error <> 0) then
    Exit;
  Result:=pIpe.Status=IP_SUCCESS;
finally
  ...
  FreeMem(pIpe);
end;

Ошибка с утечкой памяти связана с тем, что если функция IcmpSendEcho завершилась с ошибкой, то мы выходим из функции, минуя блок finally, и соответственно, не очищаем память, выделенную под ответ на ICMP-запрос, заодно не очищаем ресурсы, выделенные под WSA.
В принципе, это не очень страшно, с учетом того, сколько памяти в наше время имеется у компьютеров, да еще и с механизмом ее виртуализации. Видимо, на практике дождаться возникновения каких-то проблем было бы очень сложно 😀.
(Как верно указал мне в комментариях Иван, все это не верно, так как даже при вызове Exit в защищаемом блоке, finally все равно срабатывает. Видимо, исключением из этого правила может быть только при переходе на метку за пределами защищаемого блока. Хотя, если подумать, то наверное, это должно по хорошему приводить к ошибке или хотя бы предупреждению компиляции.)

С этой же темой связана и ошибка, точнее, отсутствие ее при превышении периода ожидания ответа. Дело в том, что IcmpSendEcho возвращает сетевые ошибки (ошибки, возникшие в процессе следования IP-пакета по маршруту), в той структуре pIpe, которую мы передали функции при ее вызове.
А вот в случае превышения времени ожидания функция
IcmpSendEcho завершается с ошибкой и код ошибки (превышено время ожидания) храниться не в структуре pIpe, а в переменной error.

15.09.2022

Trace Route

Короче, поломался тут у меня интернет. Ну, традиционно запустил ping, tracert: короче, шлюз "Сибирских сетей", к которому подключен мой роутер, иногда пропадает на несколько десятков минут, хотя физически соединение до коммутатора провайдера живое.

Пишу в тех. поддержку, понятное дело. И тут выясняю, что я отстал от жизни: ни ping, ни tracert нонче у сетевиков не котируются, им нужно специальную программку поставить и её логи выслать в тех. поддержку, что бы они могли проблему решить.
Программка PingPlotter называется. В принципе, хорошая программка, есть у нее только один недостаток: она платная. Хоть и стоит недорого, но все же, особенно в условиях санкций, имеет значение. Да и нужна-то она раз в пять лет, если не реже.
Ну и кстати, логи трассировки, полученные с помощью этой программки никак не помогли решить мою проблему: видимо, надо менять провайдера. Уже больше месяца "Сибирские сети" возятся, вроде на прошлой неделе ушла проблема, но на этой неделе снова вернулась. Хоть и не так критично, как раньше, но все же пару раз в день интернет на несколько минут пропадает.

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

В Delphi для подобных задач есть набор компонентов Indy, вроде десятой версии они у меня. Да вот незадача: компонентик для отправки ICMP запросов как-то странно работает. То есть просто пинг вроде норм, но как только ставишь маленький TTL, что бы сэмулировать трассировку маршрута, он вываливается со странной ошибкой: маленький размер буфера приема.
Проверил компонент Indy для трассировки, а не для пинга: та же шняга. Все же половина этих компонентов явно не доделана.

Порылся по интернету, нашел готовый модуль, который умеет отправлять ICMP-пинг безо всяких компонентов, используя лишь API Windows: Winsock и специальную библиотеку в довесок. Вполне себе рабочий, но вот только на Win64 он выдает ту же ошибку, что и Indy, а на Win32 норм.

Короче, фишка тут вот в чем. Windows, если в ответ вернулся ICMP-ответ с ошибкой, в буфер приема еще кладет дополнительно к обычной информации первые 8 байт из сообщения об ошибке принятого пакета. Поэтому если ICMP дошел до адресата без ошибки, то компонент Indy работает нормально, а вот если вернулась ошибка, например, "Время жизни (TTL) истекло в пути", то длины приемного буфера не хватает для помещения стандартного ответа ICMP плюс еще и сообщения об ошибки. К сожалению, видимо, разработчики компонентов Indy, видимо, не знали о такой особенности и дополнительно 8 байт в приемный буфер не выделили на случай возврата ошибочного пакета.
Но, удивительно, Win64 пишет в приемный буфер не 8, а 16 байт из сообщения об ошибке. Уж не знаю, почему. Поэтому найденный мною код и не работал в 64-битном режиме.
В общем, исходный файлик маленько подшаманил, добавил условную компиляцию, так что теперь он может работать и в 32-битном и в 64-битном режиме. Выложу здесь, может, кому пригодиться.
По хорошему, его немного надо доработать. Дело в том, что выполнения подобных ICMP задач в Windows добавлена специальная библиотека. Без нее, на голом WinSock, требуется, что бы приложение было запущено с привилегиями администратора, а с такой библиотекой можно пинговать и обычному пользователю.
Вот только на разных версиях Windows библиотека называется по разному: где-то Iphlpapi.dll, а где-то Icmp.dll. Так что по-хорошему, надо грузить библиотеки динамически, определяя, какой из вариантов на данной машине подходит.

Еще один любопытный момент связан с использованием указателей при вызове ICMP запросов. И для 32-битных, и для 64-битных систем используется одна и та же структура данных для вызовов функций, при этом все указатели, остаются, естественно 32-битными.
Как же это работает в 64-битной Windows, где указатели 64-битные? Оказывается, очень просто: 32-битный указатель расширяется (беззнаково) до 64-битного. Используется здесь тот факт, что Windows вроде всегда выделяет память под данные приложения с начала адресного пространства.
Вывод из этого безобразия: пока ваша программа вместе с данными влезает в 4 ГиБ, то все гарантированно будет работать, но как только станет больше - уже не факт ).

Ну, и кстати, в процессе узнал много интересного. Раньше думал, что, как и на Windows, на других операционках пользуются ICMP запросом Echo для пинга и трассировки маршрута. Но, оказывается, на Unix-подобных системах предпочитают отправлять UDP-запросы на неиспользуемый порт, ловя обратно ICMP-ответы по мере прохождения маршрута. Завершение маршрута определяет по ошибке о недоступности порта. В принципе, годный метод, но как быть, если порт на удаленной машине для чего-то используется? Ведь пингуя таким образом удаленную машину, мы не можем знать, какие порты на ней используются, а какие нет.

А еще бывают и более экзотические варианты пинга, на основе TCP. Но в эту сторону особо не смотрел.