• このエントリーをはてなブックマークに追加


メモ

※ 「drop イベント云々」というよりは「DataTransfer#files が」という方が正しいですかね.

Dropzone.js を用いた,ローカルファイルをブラウザにドロップすることによるファイルアップロード機能の実装を試していて,Firefox と Chrome とで,その挙動の時間にあまりに差があったので,ちょっと確認.

気になる個所

Dropzone.js のソースコードを読んでたりした結果,Dropzone#drop での e.transferData.files の部分が怪しいのではないかということに.

ごく簡単な確認

drop イベントハンドラ内で DragEvent#transferData.files を呼び出す前後の時刻差を計算してみる.これが,ドロップするファイル数に対してどのように変化するのか.

マシン

  • MacBook Pro 15-inch, Late 2011
  • プロセッサ 2.5 GHz Intel Core i7
  • メモリ 16 GB 1333 MHz DDR3
  • ソフトウェア OS X 10.9.4(13E28)

古い.

ブラウザ

  • Firefox 32
  • Chrome 37

準備したファイル

  • 2500 点の JPEG ファイル
  • 5.5GB (2.25MB / ファイル)

コード

まっさらから準備するのがメンドウだったので,実装中のアプリケーションに対して,Dropzone#drop を上書きして上記の処理を行うだけにするなどして環境を準備:

class DZX extends Dropzone
    drop: (ev) ->
        [t1, t2] = [(new Date()).getTime(), null]
 
        files = ev.dataTransfer.files   # (A)
 
        t2 = (new Date()).getTime()
        result = (t2 - t1) / 1000
        console.log result
 
class DZModel
    dz: null
    constructor: (args) ->
        {target} = args
        dz_opts =
            url:                   'foobar'
            createImageThumbnails: false
            autoProcessQueue:      false
            autoQueue:             false
        @dz = new DZX(target, dz_opts)
 
window.onload = () ->
    new DZModel(target: '#dzmodel')

この状態でリロードしてはファイルをドロップしては...を繰り返す原始的な方法.特定のファイル数につき一度ずつしか計測していません.

結果

グラフについて,横軸がファイル数,縦軸が処理前後の「時刻差」(単位は「秒」).

A

ss-1410892971

Chrome は常にほぼ 0 なのにたいして,Firefox ではファイル数の増加に伴い指数関数的に処理時間が増加しているように伺える.

B

意味があるのかどうかわからないけど,先のコードの (A) の部分を「代入しない」形にしてみる.

        ev.dataTransfer.files   # (B)
ss-1410893217

A の場合と変わりはないみたい.

C

Event#dataTransfer にだけアクセスしてみた場合:

        ev.dataTransfer   # (C)
ss-1410894391

Firefox,Chrome 両者ファイル数に関係なくほぼ 0.

TSV データ

上記 A 〜 C の TSV データ:

まとめ

Firefox では,DataTransfer#files の参照処理時間が,ファイル数に対して指数関数的に増大する傾向にあるのではないか,という気がする.

実装するアップロード機能,最低 5000 ファイルくらいのドロップには対応できるようにしたいところだけど,Dropzone.js の問題でもなさげだしどうしたものかなぁ...

  • このエントリーをはてなブックマークに追加


取り急ぎ発表資料を上げておきます.ご興味のある方は.

ちなみに壇上からの様子.

写真 2014-08-30 16 53 55

最終日キーノート発表の前枠なので,人がすごい.写真では見きれてますが,2階席からも見られているのでマジヤバイ.

発表で使用した MacBookAir,「ロジックボードの交換」のために「ピックアップ&デリバリー」による修理を依頼,20日に集荷でしたが,25日には戻ってきてくれました.バッテリまで交換されて.とにかく間に合ってよかったです.

  • このエントリーをはてなブックマークに追加


メモ

こんな感じで書いていた String::Incremental ですが,本日,この人生初めての CPAN モジュールとしてリリースしてしまいました:

minil release の後の Enter を叩くのにかなりの勇気を要したことはおいておいて,まぁこんな感じで使ってみてください:

use strict;
use warnings;
use utf8;
use String::Incremental qw( incremental_string );
use Encode;
 
my $age = incremental_string( '0x%2=', [0..9, 'a'..'f'] );
$age->set( '0x24' );
 
$age++;
 
my $msg = sprintf( 'そして本日,%s 歳と相成りました.', $age );
print encode_utf8( $msg );

より詳細は上記リンクや Pod にて.

このモジュールで,私以外の 1人くらいの何かしらの助けになれば幸いでございます.そしてツッコミやパッチもお待ちしております.

  • このエントリーをはてなブックマークに追加


はじめに

以前にこんなエントリを書きました:

Amon2 Web アプリケーションのコントローラ内とかで Data::Section::Simple::get_data_section を呼んで DATA セクションの内容を取得するようなコードを書いたのですが,ここでも,上記のような問題が発生してしまいます.

ので,その修正がてら,一対策方法としてメモしておきます.

サンプルコード

以下の内容は,このコードを基にしています.

事前準備

amon2-setup.pl --flabor=Minimum MyApp として.

サーバを起動しておきます:

carton exec -- perl script/myapp-server 2>/dev/null

アプリケーションに対してそこそこ並行して HTTP リクエストを投げるための簡単なスクリプト sandbox/req.pl:

use 5.18.0;
use warnings;
use utf8;
use Getopt::Long qw( :config posix_default no_ignore_case bundling );
use Time::HiRes ();
use HTTP::Tiny;
 
my %opts = (
    n => 10,
);
GetOptions(
    'n=i' => \$opts{n},
);
 
my $ua = HTTP::Tiny->new();
 
sub main {
    my (%args) = @_;
 
    my $pid0 = $$;
 
    for my $i ( 1 .. $args{n} ) {
        my $pid = fork();
        last if ! $pid;
    }
 
    if ( $$ == $pid0 ) {
        sleep 1;
    }
    else {
        Time::HiRes::sleep ( rand() / 100 );
        my $res = $ua->get( 'http://localhost:5000/' );
        say $res->{status};
    }
}
 
main( %opts );

問題のアプリケーション (branch master

リクエストが来たら DATA セクションを呼見込んでみて,OK なら 200,ダメなら 500 を返す,それだけです:

package MyApp::Web;
use strict;
use warnings;
use utf8;
use parent qw(MyApp Amon2::Web);
use File::Spec;
use Data::Section::Simple qw( get_data_section );
 
sub dispatch {
    my ($c) = @_;
 
    my $foo = get_data_section( 'foo' );
    my $res = $c->create_response( defined $foo ? 200 : 500 );
    $res->content_type( 'text/plain' );
    $res->content( defined $foo ? 'OK' : 'NG' );
 
    return $res;
}
 
1;
__DATA__
 
@@ foo
foobar

このアプリケーションに対して上記のスクリプトを使ってリクエストを投げてみます:

% for i in {1..10}; do echo "**** $i ****"; perl sandbox/req.pl -n 100 | grep 500; done
**** 1 ****
500
500
**** 2 ****
500
**** 3 ****
500
500
500
500
**** 4 ****
500
**** 5 ****
500
500
500
**** 6 ****
500
500
500
500
500
500
**** 7 ****
500
**** 8 ****
500
500
500
500
500
500
500
**** 9 ****
500
500
500
500
500
**** 10 ****
500
500
500

DATA セクションを読み込めていないことがあることがわかります.

とりあえず解決策 (branch solution

こんな考えを基に:

  • コンテキストクラスロード時に,各クラスの DATA セクションをまとめて読み込む
  • コンテキストオブジェクト経由で保持しているデータにアクセスできるようにする
  • あと,できれば,既存のコードをあまり変更したくない

変更内容はこんな感じ:

% g diff lib/MyApp/Web.pm
diff --git a/lib/MyApp/Web.pm b/lib/MyApp/Web.pm
index cd379ef..277192a 100644
--- a/lib/MyApp/Web.pm
+++ b/lib/MyApp/Web.pm
@@ -4,12 +4,35 @@ use warnings;
 use utf8;
 use parent qw(MyApp Amon2::Web);
 use File::Spec;
-use Data::Section::Simple qw( get_data_section );
+use Amon2::Util ();
+use Module::Find;
+use Data::Section::Simple ();
+
+__PACKAGE__->load_data_section();
+
+sub load_data_section {
+    my ($self) = @_;
+    my %data;
+    my @modules = ( __PACKAGE__, useall __PACKAGE__ );
+    for my $m ( @modules ) {
+        my $ds = Data::Section::Simple->new( $m );
+        $data{$m} = $ds->get_data_section();
+    }
+    Amon2::Util::add_method( __PACKAGE__, '__data__', sub { \%data } );
+}
+
+sub get_data_section {
+    my ($self, $key, $pkg) = @_;
+    $pkg ||= caller 0;
+    my $ret;
+    $ret = $self->__data__->{$pkg}{$key}  if $self->can( '__data__' );
+    return $ret;
+}
 
 sub dispatch {
     my ($c) = @_;
 
-    my $foo = get_data_section( 'foo' );
+    my $foo = $c->get_data_section( 'foo' );
     my $res = $c->create_response( defined $foo ? 200 : 500 );
     $res->content_type( 'text/plain' );
     $res->content( defined $foo ? 'OK' : 'NG' );

この変更で,アプリケーションの各所で get_data_section(...) していた部分は,$c->get_data_section(...) と変更するだけでよくなりました.

で,変更後のアプリケーションに対して同リクエストスクリプトを投げてみた結果:

% for i in {1..10}; do echo "**** $i ****"; perl sandbox/req.pl -n 10000 | grep 500; done
**** 1 ****
**** 2 ****
**** 3 ****
**** 4 ****
**** 5 ****
**** 6 ****
**** 7 ****
**** 8 ****
**** 9 ****
**** 10 ****

ちゃんと DATA セクションの内容を取得できているようです.(厳密には直接読み込んでいるわけではありませんが.)

おわりに

本エントリでサンプルとしてあげたのは Minimum flavor ですが,私が実際に修正したアプリケーションは Large flavor,同様の変更で対応できているようです.

  • このエントリーをはてなブックマークに追加


メモ

Mac で ImageMagick をビルドする際,それが依存するライブラリについては,homebrew で入れたものを利用してました.

あまり考えなしに brew up; brew upgrade してきたら,こんな感じ:

% ~/local/bin/convert --version
dyld: Library not loaded: /usr/local/lib/libgs.9.06.dylib
  Referenced from: /Users/issm/local/bin/convert
  Reason: image not found
[1]    68938 trace trap  ~/local/bin/convert --version

に「依存ライブラリが見つからん」とかなったり.

バージョン上げずに,というのも手だけど brew outdated で表示されるものが増えていくのはなんとも.

そんなわけで,ImageMagick(今回は 6.8.9-5) をビルドする際に,上記のような被害が起こりやすい(と個人的に思っている)ライブラリ libjpeglibpngghotstscript あとついでに libtiff あたりもビルドして,同だけが参照するような風にもっていってみます.

目的

$HOME/local/imagemagick/6.8.9-5/bin/convert のような感じで使えるようにする.

tarballs

% pwd
/Users/issm/local/imagemagick/6.8.9-5/dist
% ls -1
ImageMagick-6.8.9-5.tar.gz
ghostscript-9.14.tar.gz
jpeg-8d.tar.gz
libpng-1.6.12.tar.gz
tiff-4.0.3.tar.gz

jpeg

% CFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
LDFLAGS="-L$HOME/local/imagemagick/6.8.9-5/lib" \
CPPFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
./configure \
    --prefix=$HOME/local/imagemagick/6.8.9-5
% make
% make install

tiff

% CFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
LDFLAGS="-L$HOME/local/imagemagick/6.8.9-5/lib" \
CPPFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
./configure \
    --prefix=$HOME/local/imagemagick/6.8.9-5
% nmake
% make install

png

% CFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
LDFLAGS="-L$HOME/local/imagemagick/6.8.9-5/lib" \
CPPFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
./configure \
    --prefix=$HOME/local/imagemagick/6.8.9-5
% make
% make test
% make install

ghostscript

% CFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
LDFLAGS="-L$HOME/local/imagemagick/6.8.9-5/lib" \
CPPFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
./configure \
    --prefix=$HOME/local/imagemagick/6.8.9-5 \
    --without-x \
    --disable-cups \
    --disable-compile-inits \
    --disable-gtk
% make
% make install

ImageMagick

% PATH="$HOME/local/imagemagick/6.8.9-5/bin:$PATH" \
LDFLAGS="-L$HOME/local/imagemagick/6.8.9-5/lib" \
CPPFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
CFLAGS="-I$HOME/local/imagemagick/6.8.9-5/include" \
./configure \
    --prefix=$HOME/local/imagemagick/6.8.9-5 \
    --with-jpeg \
    --with-png \
    --without-gslib \
    --without-jbig \
    --without-wmf \
    --with-freetype \
    --enable-shared \
    --disable-static \
    --without-x \
    --disable-dependency-tracking \
    --with-lcms2 \
    --with-perl
% make
% make install

ライブラリの依存状況は次のような感じ:

% otool -L  ~/local/imagemagick/6.8.9-5/bin/convert
/Users/issm/local/imagemagick/6.8.9-5/bin/convert:
	/Users/issm/local/imagemagick/6.8.9-5/lib/libMagickCore-6.Q16.2.dylib (compatibility version 3.0.0, current version 3.0.0)
	/Users/issm/local/imagemagick/6.8.9-5/lib/libMagickWand-6.Q16.2.dylib (compatibility version 3.0.0, current version 3.0.0)
	/usr/local/lib/liblcms2.2.dylib (compatibility version 3.0.0, current version 3.6.0)
	/Users/issm/local/imagemagick/6.8.9-5/lib/libtiff.5.dylib (compatibility version 8.0.0, current version 8.0.0)
	/Users/issm/local/imagemagick/6.8.9-5/lib/libjpeg.8.dylib (compatibility version 13.0.0, current version 13.0.0)
	/Users/issm/local/imagemagick/6.8.9-5/lib/libpng16.16.dylib (compatibility version 29.0.0, current version 29.0.0)
	/usr/local/lib/libfontconfig.1.dylib (compatibility version 10.0.0, current version 10.0.0)
	/usr/local/lib/libfreetype.6.dylib (compatibility version 18.0.0, current version 18.2.0)
	/usr/lib/liblzma.5.dylib (compatibility version 6.0.0, current version 6.3.0)
	/usr/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.5)
	/usr/local/lib/libpango-1.0.0.dylib (compatibility version 3601.0.0, current version 3601.5.0)
	/usr/local/lib/libgobject-2.0.0.dylib (compatibility version 4001.0.0, current version 4001.0.0)
	/usr/local/lib/libglib-2.0.0.dylib (compatibility version 4001.0.0, current version 4001.0.0)
	/usr/local/opt/gettext/lib/libintl.8.dylib (compatibility version 10.0.0, current version 10.2.0)
	/usr/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.9.0)
	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

狙ったとおりの参照になっている感じかな.

ちなみに

./configure の際にPATH をセットしているのは,$HOME/local/imagemagick/6.8.9-5/bin/gs を通すため.

./configure の result:

                  Option                        Value
-------------------------------------------------------------------------------
Shared libraries  --enable-shared=yes		yes
Static libraries  --enable-static=no		no
Module support    --with-modules=no		no
GNU ld            --with-gnu-ld=no		no
Quantum depth     --with-quantum-depth=16	16
High Dynamic Range Imagery
                  --enable-hdri=no		no
 
Install documentation:		yes
 
Delegate Configuration:
BZLIB             --with-bzlib=yes		yes
Autotrace         --with-autotrace=no		no
Dejavu fonts      --with-dejavu-font-dir=default	none
DJVU              --with-djvu=yes		no
DPS               --with-dps=yes		no
FFTW              --with-fftw=yes		no
FlashPIX          --with-fpx=yes		no
FontConfig        --with-fontconfig=yes		yes
FreeType          --with-freetype=yes		yes
GhostPCL          None				pcl6 (unknown)
GhostXPS          None				gxps (unknown)
Ghostscript       None				gs (9.14)
Ghostscript fonts --with-gs-font-dir=default	/usr/local/share/ghostscript/fontsx/
Ghostscript lib   --with-gslib=no		no
Graphviz          --with-gvc=no
JBIG              --with-jbig=no		no
JPEG v1           --with-jpeg=yes		yes
JPEG-2000         --with-jp2=
LCMS v1           --with-lcms=no		no
LCMS v2           --with-lcms2=yes		yes
LQR               --with-lqr=yes		no
LTDL              --with-ltdl=yes		no
LZMA              --with-lzma=yes		yes
Magick++          --with-magick-plus-plus=yes	yes
OpenEXR           --with-openexr=yes		no
OpenJP2           --with-openjp2=yes		no
PANGO             --with-pango=yes		yes
PERL              --with-perl=yes		/Users/issm/.plenv/shims/perl
PNG               --with-png=yes		yes
RSVG              --with-rsvg=no		no
TIFF              --with-tiff=yes		yes
WEBP              --with-webp=yes		no
Windows fonts     --with-windows-font-dir=	none
WMF               --with-wmf=no		no
X11               --with-x=no			no
XML               --with-xml=yes		yes
ZLIB              --with-zlib=yes		yes
 
X11 Configuration:
      X_CFLAGS        =
      X_PRE_LIBS      =
      X_LIBS          =
      X_EXTRA_LIBS    =
 
Options used to compile and link:
  PREFIX          = /Users/issm/local/imagemagick/6.8.9-5
  EXEC-PREFIX     = /Users/issm/local/imagemagick/6.8.9-5
  VERSION         = 6.8.9
  CC              = gcc
  CFLAGS          = -I/usr/local/Cellar/freetype/2.5.3_1/include/freetype2 -I/Users/issm/local/imagemagick/6.8.9-5/include -Wall -march=corei7-avx -fexceptions -D_FORTIFY_SOURCE=0 -D_THREAD_SAFE -pthread -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16
  CPPFLAGS        = -D_REENTRANT -I/usr/local/Cellar/pango/1.36.5/include/pango-1.0 -I/usr/local/Cellar/glib/2.40.0_1/include/glib-2.0 -I/usr/local/Cellar/glib/2.40.0_1/lib/glib-2.0/include -I/usr/local/opt/gettext/include -I/usr/local/Cellar/xz/5.0.5/include -I/usr/local/Cellar/fontconfig/2.11.1/include -I/usr/local/Cellar/freetype/2.5.3_1/include/freetype2 -I/Users/issm/local/imagemagick/6.8.9-5/include -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libxml2
  PCFLAGS         =
  DEFS            = -DHAVE_CONFIG_H
  LDFLAGS         = -L/Users/issm/local/imagemagick/6.8.9-5/lib -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/lib
  LIBS            =
  CXX             = g++
  CXXFLAGS        = -g -O2 -D_THREAD_SAFE -pthread
  FEATURES        = DPC
  DELEGATES       = bzlib mpeg fontconfig freetype jng jpeg lcms lzma pango png ps tiff xml zlib

のうち, “Ghostscript” の部分に影響してくる感じ:

Ghostscript       None				gs (9.14)

Image::Magick

plenv にて,PLENV_VERSION=5.18@default として入れた perl に対してモジュールをインストール.

% cd ~/local/imagemagick/6.8.9-5/dist/ImageMagick-6.8.9-5
% cp -r PerlMagick PerlMagick.5.18@default
% cd PerlMagick.5.18@default

Makefile.PL を編集.

$LIBS_magick-L/Users/issm/.plenv/versions/5.18@default/lib/perl5/5.18.2/darwin-2level/CORE を追加します:

diff --git a/Makefile.PL b/Makefile.PL
index a01c11d..d17b463 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -162,7 +162,7 @@ foreach my $delegate (@tested_delegates) {
 
 # defaults for LIBS & INC & CCFLAGS params that we later pass to Writemakefile
 my $INC_magick = '-I../ -I.. -D_REENTRANT -I/usr/local/Cellar/pango/1.36.5/include/pango-1.0 -I/usr/local/Cellar/glib/2.40.0_1/include/glib-2.0 -I/usr/local/Cellar/glib/2.40.0_1/lib/glib-2.0/include -I/usr/local/opt/gettext/include -I/usr/local/Cellar/xz/5.0.5/include -I/usr/local/Cellar/fontconfig/2.11.1/include -I/usr/local/Cellar/freetype/2.5.3_1/include/freetype2 -I/Users/issm/local/imagemagick/6.8.9-5/include -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libxml2 -I"' . $Config{'usrinc'} . '/ImageMagick"';
-my $LIBS_magick = '-L../magick/.libs -lMagickCore-6.Q16 -lperl -lm';
+my $LIBS_magick = '-L/Users/issm/.plenv/versions/5.18@default/lib/perl5/5.18.2/darwin-2level/CORE -L../magick/.libs -lMagickCore-6.Q16 -lperl -lm';
 my $CCFLAGS_magick = "$Config{'ccflags'} -I/usr/local/Cellar/freetype/2.5.3_1/include/freetype2 -I/Users/issm/local/imagemagick/6.8.9-5/include -Wall -march=corei7-avx -fexceptions -D_FORTIFY_SOURCE=0 -D_THREAD_SAFE -pthread -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16";
 my $LDFLAGS_magick   = "-L../magick/.libs -lMagickCore-6.Q16 $Config{'ldflags'} -L/Users/issm/local/imagemagick/6.8.9-5/lib -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/lib";
 my $LDDLFLAGS_magick = "-L../magick/.libs -lMagickCore-6.Q16 $Config{'lddlflags'} -L/Users/issm/local/imagemagick/6.8.9-5/lib -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/lib";

そして:

% PLENV_VERSION=5.18@default perl Makefile.PL
% make
% make test

そして:

% make install
Files found in blib/arch: installing files in blib/lib into architecture dependent library tree
Installing /Users/issm/.plenv/versions/5.18@default/lib/perl5/site_perl/5.18.2/darwin-2level/auto/Image/Magick/Magick.bundle
Appending installation info to /Users/issm/.plenv/versions/5.18@default/lib/perl5/5.18.2/darwin-2level/perllocal.pod

これで,

% PLENV_VERSION=5.18@default perl -MImage::Magick -le 1

としても,Can't locate とか言われない.

1 of 12012345...102030...120