2015-07-23 15 views
10

tego kodu:Benchmarking plik utf8 czytać - wyjaśnienie różnic

#!/usr/bin/env perl 

use 5.016; 
use warnings; 
use autodie; 
use Path::Tiny; 
use Encode; 
use Benchmark qw(:all); 

my $cnt = 10_000; 
my $utf = 'utf8.txt'; 

my $res = timethese($cnt, { 
    'open-UTF-8' => sub { 
     open my $fhu, '<:encoding(UTF-8)', $utf; 
     my $stru = do { local $/; <$fhu>}; 
     close $fhu; 
    }, 
    'open-utf8' => sub { 
     open my $fhu, '<:utf8', $utf; 
     my $stru = do { local $/; <$fhu>}; 
     close $fhu; 
    }, 
    'decode-utf8' => sub { 
     open my $fhu, '<', $utf; 
     my $stru = decode('utf8', do { local $/; <$fhu>}); 
     close $fhu; 
    }, 
    'decode-UTF-8' => sub { 
     open my $fhu, '<', $utf; 
     my $stru = decode('UTF-8', do { local $/; <$fhu>}); 
     close $fhu; 
    }, 
    'ptiny' => sub { 
     my $stru = path($utf)->slurp_utf8; 
    }, 
}); 
cmpthese $res; 

utf8.txt (ok 175kb) zawiera 1000 linii utf8 zakodowany/ASCII znaków, takich jak:

9áäčďéěíĺľňóôöőŕřšťúůüűýž ÁÄČĎÉĚÍĹĽŇÓÔÖŐŔŘŠŤÚŮÜŰÝŽ aáäbcčdďeéěfghiíjkľĺmnňoóôöőpqrŕřsštťuúůüűvwxyýzž 

Uruchamianie powyżej, na moim notesie jest:

Benchmark: timing 10000 iterations of decode-UTF-8, decode-utf8, open-UTF-8, open-utf8, ptiny... 
decode-UTF-8: 47 wallclock secs (46.83 usr + 0.87 sys = 47.70 CPU) @ 209.64/s (n=10000) 
decode-utf8: 48 wallclock secs (46.62 usr + 0.90 sys = 47.52 CPU) @ 210.44/s (n=10000) 
    open-UTF-8: 60 wallclock secs (57.82 usr + 1.20 sys = 59.02 CPU) @ 169.43/s (n=10000) 
    open-utf8: 7 wallclock secs (6.57 usr + 0.70 sys = 7.27 CPU) @ 1375.52/s (n=10000) 
     ptiny: 7 wallclock secs (5.98 usr + 0.52 sys = 6.50 CPU) @ 1538.46/s (n=10000) 
       Rate open-UTF-8 decode-UTF-8 decode-utf8 open-utf8  ptiny 
open-UTF-8 169/s   --   -19%  -19%  -88%  -89% 
decode-UTF-8 210/s   24%   --   -0%  -85%  -86% 
decode-utf8 210/s   24%   0%   --  -85%  -86% 
open-utf8 1376/s  712%   556%  554%   --  -11% 
ptiny  1538/s  808%   634%  631%   12%   -- 

Dla mnie zaskakujące, więc pytania:

  • pierwsza - coś jest nie tak z powyższym kodem?

Jeśli jest ok,

  • dlaczego ogromna różnica pomiędzy wyraźnej UTF-8 i zrelaksowany utf8 ale tylko u na poziomie IO-Layer (<:utf8 i <:encoding(UTF-8)? Tak,
  • dlaczego różnica nie tak duże, gdy decode('UTF-8' i ?
  • , dlaczego leniwy - poziom dekodowania warstwy IO jest znacznie o wiele szybszy, jako jawny również leniwy decode('utf8?
  • i jakie "niebezpieczeństwo" może być przy użyciu zrelaksowanego (szybkiego) "utf8" kontra dokładnego (wolnego) "UTF-8"?

  • i wreszcie naprawdę nie pytanie - muszę sprawdzić ścieżkę Code :: Tiny - jak to jest najszybszy ...

Koperta:

  • Perl v5.22.0 - perlbrew (gwintowane)
  • OSX - Darwin Kernel wersja 14.4.0 (Yosemite)
  • notebook stary - MacBook Pro (13-calowy, połowa 2010) - rdzeń-2 Duo 2,4 GHz, 8GB, powolny HDD
+0

Her są moje wyniki (Ubuntu 14.10, Lenovo krawędzi 540, Intel (R) Core (TM) CPU @ 2.30GHz i7-4712MQ): http: // Pastebin. com/kUst2Bim. Plik wejściowy: http://pastebin.com/7fMnpFLQ –

+2

@ HåkonHægland Podobne. Również ogromna różnica między leniwym/ścisłym 'na warstwie' a nie dużym różnicem pomiędzy leniwym/ścisłym' dekodowaniem'. Zaskocz ścieżkę :: malutka. – jm666

+1

@ jm666, Powodem jest prawdopodobnie to, że Håkon Hægland nie ma zainstalowanego Unicode :: UTF8. – chansen

Odpowiedz

8

: utf8

perlio :utf8 warstwa jest warstwą pseudo, to po prostu flaga na perlio obsłużyć której OP wykrycia. Zachowanie zmienia się w zależności od stosowanej OP:

odczytu() sysread() i recv():

W implementation nie wykonuje walidację sekwencji utf8. Implementacja only checks the prefix octet sekwencji utf8 do zliczania liczby odczytanych sekwencji utf8.

readline():

implementation sprawdza oktetów odczytu if the warnings kategorię 'utf8' jest w rzeczywistości i wysyła ostrzeżenie, jeśli oktety odczytu zawiera źle utworzone utf8. Zastosowana procedura sprawdzania poprawności jest taka sama, jak użyta w utf8::decode().

Flaga/warstwa ": utf8" nigdy nie powinny być używane do odczytu, chyba że jesteś skłonny zaakceptować Złe sformatowanie UTF-X, które może prowadzić do security issues lub błędów segmentacji.

: kodowanie

perlio :encoding warstwa zapewnia PerlIO::encoding realizujący przyrostowej ramy dekoderem podklas Encode::Encoding. implementation wywołuje podklasę Perl/XS, wywołując metodę dla każdego przyrostowego dekodowania. Bufory są kopiowane między warstwą a podklasą.

utf8 vs UTF-8

Postać kodowania utf8 rozszerzeniem UTF-8 tworzą kodowania wskazanym przez Unicode Consortium. Forma kodowania utf8 akceptuje kodowane punkty kodowe źle sformułowane w formie kodującej UTF-8, takie jak surrogates i punkty kodowe powyżej U + 10FFFF. Non-characters należy również unikać, mimo że Unicode recently changed ich umysł. Kodowanie utf8 nie powinno być używane do wymiany, to wewnętrzne kodowanie Perla. Zamiast tego użyj formatu kodowania UTF-8.

Wzorzec slurping UTF-8 plikowi

modułów stosowanych w teście:

PerlIO::encoding, PerlIO::utf8_strict, Encode i Unicode::UTF8.

Poniższy kod jest również dostępny pod numerem gist.github.com.

#!/usr/bin/perl 

use strict; 
use warnings; 

use Benchmark  qw[]; 
use Config  qw[%Config]; 
use IO::Dir  qw[]; 
use IO::File  qw[SEEK_SET]; 

use Encode    qw[]; 
use Unicode::UTF8  qw[]; 
use PerlIO::encoding qw[]; 
use PerlIO::utf8_strict qw[]; 

# https://github.com/chansen/p5-unicode-utf8/tree/master/benchmarks/data 
my $dir = 'benchmarks/data'; 
my @docs = do { 
    my $d = IO::Dir->new($dir) 
     or die qq/Could not open directory '$dir': $!/; 
    sort grep { /^[a-z]{2}\.txt/ } $d->read; 
}; 

printf "perl:    %s (%s %s)\n", $], @Config{qw[osname osvers]}; 
printf "Encode:    %s\n", Encode->VERSION; 
printf "Unicode::UTF8:  %s\n", Unicode::UTF8->VERSION; 
printf "PerlIO::encoding: %s\n", PerlIO::encoding->VERSION; 
printf "PerlIO::utf8_strict: %s\n", PerlIO::utf8_strict->VERSION; 

foreach my $doc (@docs) { 

    my $octets = do { 
     open my $fh, '<:raw', "$dir/$doc" or die $!; 
     local $/; <$fh>; 
    }; 

    my $string = Unicode::UTF8::decode_utf8($octets); 

    my @ranges = (
     [ 0x00,  0x7F, qr/[\x{00}-\x{7F}]/  ], 
     [ 0x80, 0x7FF, qr/[\x{80}-\x{7FF}]/  ], 
     [ 0x800, 0xFFFF, qr/[\x{800}-\x{FFFF}]/  ], 
     [ 0x10000, 0x10FFFF, qr/[\x{10000}-\x{10FFFF}]/ ], 
    ); 

    my @out; 
    foreach my $r (@ranges) { 
     my ($start, $end, $regexp) = @$r; 
     my $count =() = $string =~ m/$regexp/g; 
     push @out, sprintf "U+%.4X..U+%.4X: %d", $start, $end, $count 
      if $count; 
    } 

    printf "\n\n%s: Size: %d Code points: %d (%s)\n", 
     $doc, length $octets, length $string, join ' ', @out; 

    open my $fh_raw, '<:raw', \$octets 
     or die qq/Could not open a :raw fh: '$!'/; 
    open my $fh_encoding, '<:encoding(UTF-8)', \$octets 
     or die qq/Could not open a :encoding fh: '$!'/; 
    open my $fh_utf8_strict, '<:utf8_strict', \$octets 
     or die qq/Could not open a :utf8_strict fh: '$!'/; 

    Benchmark::cmpthese(-10, { 
     ':encoding(UTF-8)' => sub { 
      my $data = do { local $/; <$fh_encoding> }; 
      seek($fh_encoding, 0, SEEK_SET) 
       or die qq/Could not rewind fh: '$!'/; 
     }, 
     ':utf8_strict' => sub { 
      my $data = do { local $/; <$fh_utf8_strict> }; 
      seek($fh_utf8_strict, 0, SEEK_SET) 
       or die qq/Could not rewind fh: '$!'/; 
     }, 
     'Encode' => sub { 
      my $data = Encode::decode('UTF-8', do { local $/; scalar <$fh_raw> }, Encode::FB_CROAK|Encode::LEAVE_SRC); 
      seek($fh_raw, 0, SEEK_SET) 
      or die qq/Could not rewind fh: '$!'/; 
     },   
     'Unicode::UTF8' => sub { 
      my $data = Unicode::UTF8::decode_utf8(do { local $/; scalar <$fh_raw> }); 
      seek($fh_raw, 0, SEEK_SET) 
      or die qq/Could not rewind fh: '$!'/; 
     }, 
    }); 
} 

Wyniki:

$ perl benchmarks/slurp.pl 
perl:    5.023001 (darwin 14.4.0) 
Encode:    2.75 
Unicode::UTF8:  0.60 
PerlIO::encoding: 0.21 
PerlIO::utf8_strict: 0.006 


ar.txt: Size: 25918 Code points: 14308 (U+0000..U+007F: 2698 U+0080..U+07FF: 11610) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 3058/s    --  -19%   -73%   -87% 
Encode   3754/s    23%   --   -67%   -84% 
:utf8_strict  11361/s    272%  203%   --   -52% 
Unicode::UTF8 23620/s    672%  529%   108%   -- 


el.txt: Size: 103974 Code points: 58748 (U+0000..U+007F: 13560 U+0080..U+07FF: 45150 U+0800..U+FFFF: 38) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 780/s    --   -19%   -73%   -86% 
Encode   958/s    23%   --   -66%   -83% 
:utf8_strict  2855/s    266%   198%   --   -48% 
Unicode::UTF8 5498/s    605%   474%   93%   -- 


en.txt: Size: 82171 Code points: 82055 (U+0000..U+007F: 81988 U+0080..U+07FF: 18 U+0800..U+FFFF: 49) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 1111/s    --  -16%   -90%   -96% 
Encode   1327/s    19%   --   -88%   -95% 
:utf8_strict  11446/s    931%  763%   --   -60% 
Unicode::UTF8 28635/s   2478%  2058%   150%   -- 


ja.txt: Size: 180109 Code points: 64655 (U+0000..U+007F: 6913 U+0080..U+07FF: 30 U+0800..U+FFFF: 57712) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 553/s    --   -27%   -72%   -91% 
Encode   757/s    37%   --   -61%   -87% 
:utf8_strict  1960/s    254%   159%   --   -67% 
Unicode::UTF8 5915/s    970%   682%   202%   -- 


lv.txt: Size: 138397 Code points: 127160 (U+0000..U+007F: 117031 U+0080..U+07FF: 9021 U+0800..U+FFFF: 1108) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 605/s    --   -19%   -80%   -91% 
Encode   746/s    23%   --   -75%   -88% 
:utf8_strict  3043/s    403%   308%   --   -53% 
Unicode::UTF8 6453/s    967%   765%   112%   -- 


ru.txt: Size: 151633 Code points: 85266 (U+0000..U+007F: 19263 U+0080..U+07FF: 65639 U+0800..U+FFFF: 364) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 542/s    --   -19%   -73%   -86% 
Encode   673/s    24%   --   -66%   -83% 
:utf8_strict  2001/s    269%   197%   --   -50% 
Unicode::UTF8 4010/s    640%   496%   100%   -- 


sv.txt: Size: 96449 Code points: 92894 (U+0000..U+007F: 89510 U+0080..U+07FF: 3213 U+0800..U+FFFF: 171) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 923/s    --  -17%   -85%   -93% 
Encode   1109/s    20%   --   -82%   -92% 
:utf8_strict  5998/s    550%  441%   --   -56% 
Unicode::UTF8 13604/s   1374%  1127%   127%   -- 


zh.txt: Size: 62891 Code points: 24519 (U+0000..U+007F: 5317 U+0080..U+07FF: 32 U+0800..U+FFFF: 19170) 
        Rate :encoding(UTF-8)  Encode :utf8_strict Unicode::UTF8 
:encoding(UTF-8) 1630/s    --  -23%   -75%   -87% 
Encode   2104/s    29%   --   -68%   -83% 
:utf8_strict  6549/s    302%  211%   --   -48% 
Unicode::UTF8 12630/s    675%  500%   93%   -- 
+0

Po prostu "używaj ostrzeżeń qw (FATAL utf8)", a zwykła warstwa ': utf8' jest idealnie w porządku. Możesz także chcieć oddzielnie kontrolować subwarstwy 'surrogate',' non_unicode', 'nonchar' - coś, co nie jest nawet możliwe przy użyciu wolnego modułu. – tchrist

+3

@tchrist, nie jest, pod-ostrzeżenia mają zastosowanie tylko na wyjściu, na wejściu za pomocą readline() nie mają żadnego efektu. Kodowanie utf8 akceptuje źle sformułowane UTF-8, takie jak kodowane surogaty. Spróbuj tego: '$ perl -E 'otwórz moje $ fh," <: utf8 ", \" \ xED \ xB0 \ x80 "lub umrzyj; printf "U +%. 4X \ n", ord readline ($ fh) ", jest całkowicie cichy i zakodowany U + DC00 jest źle sformułowany przez standard Unicode. – chansen

+2

Dobroć łaskawa! – tchrist