URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 4179
[ Назад ]

Исходное сообщение
"inet_ntoa и inet_aton"

Отправлено rWizard , 14-Апр-05 20:16 
В нескольких программах (perl) мне нужно ( напрмер, для скорости сравнения )
испольковать ip аддреса в численном виде.
идею перевода заимствовал из mysql

sub to_num {
    my @s = split /\./, $_[0];
    my $num = $s[0]*256**3 +  $s[1]*256**2 + $s[2]*256 + $s[3];
    return $num;
}

но вот, с обратным переводом - проблемма. Я для него ипользу приведенный ниже алогитм, который, мягко говоря "не оптимальный". Есть-ли более красивый способ?

sub from_num {
    my @ipc; # ip in chars
    my $curr = $_[0];
    push @ipc, int($curr/256**3);
    $curr -= $ipc[0]*256**3;
    push @ipc, int($curr/256**2);
    $curr -= $ipc[1]*256**2;
    push @ipc, int($curr/256);
    $curr -= $ipc[2]*256;
    push @ipc, $curr;
    return join('.',@ipc);
}


Содержание

Сообщения в этом обсуждении
"inet_ntoa и inet_aton"
Отправлено madskull , 14-Апр-05 20:47 
Странное решение они используют... может быть, просто для того, чтобы избежать неверного сравнивания адресов вида 1.2.3.4 и 001.002.003.004?
Потому что в perl'e расходы на преобразования будут намного больше, чем сравнение строк.
Если мне склероз не изменяет, в perl'e все хранится в виде строк, так что нет нужды переводить из строки в числа.

Ну, а если уж очень хочется, то может так красивше?
sub from_num {
   my $n = shift;
    return int($n/16777216).".".int($n%16777216/65536).".".
        int($n%65536/256).".".int($n%256)
}

Да, забыл написать, что может еще правильнее будет использовать inet_aton/inet_ntoa
из пакета Socket?


"inet_ntoa и inet_aton"
Отправлено rWizard , 14-Апр-05 21:50 
>Странное решение они используют... может быть, просто для того, чтобы избежать неверного
>сравнивания адресов вида 1.2.3.4 и 001.002.003.004?
>Потому что в perl'e расходы на преобразования будут намного больше, чем сравнение
>строк.
>Если мне склероз не изменяет, в perl'e все хранится в виде строк,
>так что нет нужды переводить из строки в числа.

Быстрее происходит сарвнение больше/меньше, наприер для определения принадлежности ip к диапозону. И, наприпер, в таком виде удобнее хранить в sqlite базе (маньше места, быстрее выборки, если везде - integer).

>Ну, а если уж очень хочется, то может так красивше?
>sub from_num {
>   my $n = shift;
>    return int($n/16777216).".".int($n777216/65536).".".
>        int($ne536/256).".".int($n%6)
>}
Сасибо. Замеров скорости не проводил, но на взгляд это должно работать быстрее.

>Да, забыл написать, что может еще правильнее будет использовать inet_aton/inet_ntoa
>из пакета Socket?
он возвращет "opaque string (if programming in C, struct in_addr)" а, это для мого случая не подходит.


"inet_ntoa и inet_aton"
Отправлено ACCA , 14-Апр-05 21:23 
sub from_num {
  return pack("C4", @_);
}

Только оно (как и твой алгоритм) у тебя не будет работать на другой архитектуре.


"inet_ntoa и inet_aton"
Отправлено rWizard , 15-Апр-05 00:37 
>sub from_num {
>  return pack("C4", @_);
>}
на архитекту, отличной от x86? можете объяснить подробнее?



"inet_ntoa и inet_aton"
Отправлено ACCA , 19-Апр-05 08:13 
>на архитекту, отличной от x86? можете объяснить подробнее?

Проблема в том, как выглядит число, скажем 256, в двоичном виде.

Его можно записать как 00000001 00000000 (старший байт в младшем адресе), либо как 00000000 00000001 (младший байт в младшем адресе). В архитектуре DEC (и последователях вроде SPARC) первым идёт старший байт (big endian). В архитектуре IBM (и последователях вроде x86) первым идёт младший байт (little endian).

В результате адрес 192.1.2.3 на x86 выглядит как 0x030201C0, а на SPARC как 0xC0010203. inet_ntoa и inet_aton на любой архитектуре преобразуют строку в "правильное сетевое" число (в действительности big endian).

Самодельные функции должны пользоваться системными вызовами htonl и ntohl, чтобы делать так же.


"inet_ntoa и inet_aton"
Отправлено qq , 15-Апр-05 21:54 
>sub from_num {
>  return pack("C4", @_);
>}
>
>Только оно (как и твой алгоритм) у тебя не будет работать на
>другой архитектуре.


ну, у тебя получится строка из 4 байт
а человеку надо число..
так что

perl -e 'printf("result:%X\n",unpack("N",pack("C4",split(/\./,$ARGV[0]))))' 254.128.64.32
result:FE804020

и вполне себе портабельно :)


"inet_ntoa и inet_aton"
Отправлено sashacrane , 19-Апр-05 03:35 
А такой вариант?
Полагаю, что операции с побитовыми сдвигами и AND'ами НАМНОГО быстрее работают.

sub ipToNum(){
    my $sIP = shift;
    my $res;
    $res=($1<<24)+($2<<16)+($3<<8)+$4    if $sIP =~ m/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
    $res=($1<<24)+($2<<16)+($3<<8)    if $sIP =~ m/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
    $res=($1<<24)+($2<<16)        if $sIP =~ m/^(\d{1,3})\.(\d{1,3})$/;
    $res=($1<<24)            if $sIP =~ m/^(\d{1,3})$/;
    return $res;
}

sub ipToStr(){
    my $nIP = shift;
    my $res = (($nIP>>24) & 255) .".". (($nIP>>16) & 255) .".". (($nIP>>8) & 255) .".". ($nIP & 255);
    return $res;
}


"inet_ntoa и inet_aton"
Отправлено rWizard , 20-Апр-05 22:29 
Вот результат тестов на скорость:
To numeric
Original: 4.419041
By sashacrane: 8.723555

From  numeric
Original: 6.641646
By madskull: 3.943649
By sashacrane: 3.610706


"inet_ntoa и inet_aton"
Отправлено sashacrane , 21-Апр-05 05:32 
А вот если эти строки убрать будет бустрее, но функция будет работать только для полного IP.
    $res=($1<<24)+($2<<16)+($3<<8) if $sIP =~ m/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
    $res=($1<<24)+($2<<16) if $sIP =~ m/^(\d{1,3})\.(\d{1,3})$/;
    $res=($1<<24) if $sIP =~ m/^(\d{1,3})$/;

"inet_ntoa и inet_aton"
Отправлено rWizard , 21-Апр-05 13:02 
не сильно лучше стало.
To numeric
Original: 3.930417
By sashacrane: 4.403279

PS кто-нибуть знает имет-ли спысл учитывать накладные расходы на вызов процедуры?


"inet_ntoa и inet_aton"
Отправлено qq , 21-Апр-05 18:52 
ip to num на миллион адресов:

вариант rWizard - 7.240
вариант твой - 11.223
вариант мой - 5.556

sub ipToNum(){
    my $sIP = shift;
    return unpack("N",pack("C4",split(/\./,$sIP)));
}


"inet_ntoa и inet_aton"
Отправлено qq , 21-Апр-05 19:16 
еще чуть-чуть можно ускорить(4.820), убрав двойные кавычки и не используя локальную переменную:

sub ipToNum(){
     return unpack('N',pack('C4',split(/\./,$_[0])));
}



"inet_ntoa и inet_aton"
Отправлено rWizard , 21-Апр-05 19:26 
проблемма в том, что
# perl -e'print unpack('N',pack('C4',split(/\./,'127.0.0.1')));print"\n"'  
0

или я что-то не понял?
что возвращает твоя процедура?


"inet_ntoa и inet_aton"
Отправлено qq , 21-Апр-05 19:31 
>проблемма в том, что
># perl -e'print unpack('N',pack('C4',split(/\./,'127.0.0.1')));print"\n"'
>0
>
>или я что-то не понял?
>что возвращает твоя процедура?


$ perl -e 'print  unpack("N",pack("C4",split(/\./,"127.0.0.1"))) ."\n"'
2130706433

на кавычки посмотри...
ты ж в командной строке делаешь, perl -e ' ... '


"inet_ntoa и inet_aton"
Отправлено qq , 21-Апр-05 19:28 
>ip to num на миллион адресов:
>
>вариант rWizard - 7.240
>вариант твой - 11.223
с убранными строками (только для полного ip) вариант sashacrane - 7.203


"inet_ntoa и inet_aton"
Отправлено rWizard , 22-Апр-05 16:11 
в итоге:
To numeric
By rWizard: 4.448895
By qq: 2.827952
By sashacrane: 5.078883

From  numeric
By rWizard: 5.27202
By madskull: 3.300532
By sashacrane: 2.67985

те, самые быстрые
sub ipToNum($){
     return unpack('N',pack('C4',split(/\./,$_[0])));
}

sub ipToStr(){
    my $nIP = shift;
    my $res = (($nIP>>24) & 255) .".". (($nIP>>16) & 255) .".". (($nIP>>8) & 255) .".". ($nIP & 255);
    return $res;
}

Спасибо всем участвавашим.


"inet_ntoa и inet_aton"
Отправлено qq , 22-Апр-05 19:26 
>те, самые быстрые
>sub ipToNum($){
>     return unpack('N',pack('C4',split(/\./,$_[0])));
>}
>
>sub ipToStr(){
>    my $nIP = shift;
>    my $res = (($nIP>>24) & 255) .".". (($nIP>>16) & 255) .".". (($nIP>>8) & 255) .".". ($nIP & 255);
>    return $res;
>}
>
>Спасибо всем участвавашим.

ну, самыс быстрым ip_to_num всеже будет использование inet_aton из модуля Socket.

use Socket;
sub ip_to_num {
    return unpack('N',inet_aton($_[0]));
}


однако num_to_ip все же быстрее с использованием сдвигов, как сделал sashacrane - т.к. при использовании inet_ntoa надо сначала число перевести в строку из 4 байт, чтобы inet_ntoa это скушал. Если не делать этот pack - то разница исчезает.