2015-07-23 11 views
10

Avere questo codice:file di utf8 Benchmarking leggere - spiegazione delle differenze

#!/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; 

il utf8.txt (circa 175kb) contiene 1000 righe di utf8 codificato caratteri/ASCII, come:

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

Esecuzione del sopra, sul mio quaderno:

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%   -- 

Per me sorprendente, quindi le domande:

  • prima - c'è qualcosa che non va nel codice sopra?

Se è ok,

  • il motivo per cui l'enorme differenza tra esplicita UTF-8 e rilassato utf8 ma solo al a livello di IO-strato (<:utf8 e <:encoding(UTF-8)? Quindi,
  • perché la differenza non così grande quando decode('UTF-8' e decode('utf8'?
  • perché il livello di decodifica del livello di IO-strato è molto più veloce dell'esplicito anche pigro decode('utf8?
  • e quale "pericolo" potrebbe utilizzare il rilassato (veloce) "utf8" vs esatto (lento) "UTF-8"?

  • e, infine, non è davvero una domanda - devo controllare il percorso :: Codice piccolo - come è il più veloce ...

Env:

  • perl v5.22.0 - perlbrew (filettata)
  • OSX - Darwin kernel Version 14.4.0: (Yosemite)
  • notebook vecchio - MacBook Pro (13 pollici, Metà 2010) - core-2-duo, 2.4Ghz, 8GB, lento HDD
+0

lei ci sono i miei risultati (Ubuntu 14.10, Lenovo bordo 540, Intel (R) core (TM) i7-4712MQ CPU @ 2.30GHz): http: // pastebin. com/kUst2Bim. File di input: http://pastebin.com/7fMnpFLQ –

+2

@ HåkonHægland Simile. Anche diff enorme tra lazy/strict 'sul layer' e non diff diff tra pigro/strict' decode'. Sorprendi il percorso :: minuscolo. – jm666

+1

@ jm666, Il motivo è probabilmente dovuto al fatto che Håkon Hægland non ha Unicode :: UTF8 installato. – chansen

risposta

8

: utf8

Lo strato :utf8 PerlIO è uno strato pseudo, è semplicemente una bandiera sulla PerlIO maniglia che il PO rilevare. Il comportamento varia a seconda della OP usate:

read(), sysread() e recv():

I implementation esegue alcuna convalida delle sequenze utf8. L'implementazione only checks the prefix octet della sequenza utf8 per contare il numero di sequenze di utf8 lette.

readline():

Il implementation convalida l'ottetti di lettura if the warnings categoria 'utf8' è a tutti gli effetti ed emette un avviso se gli ottetti di lettura contiene utf8 mal formati. La procedura di convalida utilizzata è la stessa utilizzata in utf8::decode().

Il flag/layer ': utf8' non deve mai essere utilizzato per la lettura a meno che non si desideri accettare UTF-X di formato non corretto che potrebbe portare a errori di segmentazione security issues.

: codifica

Lo strato :encoding PerlIO è provvisto da PerlIO::encoding che implementa un quadro decodificatore incrementale per sottoclassi di Encode::Encoding. Il numero implementation richiama la sottoclasse Perl/XS richiamando un metodo per ciascuna decodifica incrementale. I buffer vengono copiati tra il livello e la sottoclasse.

utf8 vs UTF-8

Il modulo di codifica utf8 è un sovrainsieme del formato di codifica UTF-8 specificata dal Unicode Consorzio. Il form di codifica utf8 accetta punti di codice codificati che sono mal formati nel modulo di codifica UTF-8, come surrogates e punti di codice sopra U + 10FFFF. Non-characters dovrebbe essere evitato, anche se Unicode recently changed mente. La codifica utf8 non deve essere usata per l'interscambio, è la codifica interna di Perl. Utilizzare invece il modulo di codifica UTF-8.

benchmark di slurping UTF-8 file codificato

moduli utilizzati nel benchmark:

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

Il seguente codice è disponibile anche su 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: '$!'/; 
     }, 
    }); 
} 

Risultati:

$ 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

Basta usare 'warnings qw (FATAL utf8)' e il normale layer ': utf8' è perfettamente a posto. Potresti anche voler controllare separatamente i surrogati 'surrogate',' non_unicode', 'nonchar' - qualcosa che non è nemmeno possibile quando usi il modulo lento. – tchrist

+3

@tchrist, non lo è, i subwarnings si applicano solo sull'output, sull'input usando readline() non hanno alcun effetto. La codifica utf8 per accetta UTF-8 malformata, come i surrogati codificati. Prova questo: '$ $ perl -E 'apri my $ fh," <: utf8 ", \" \ xED \ xB0 \ x80 "o muori; printf "U +%. 4X \ n", ord readline ($ fh) '', è completamente silenzioso e un U + DC00 codificato non è formato dallo standard Unicode. – chansen

+2

Bontà gentile! – tchrist