読者です 読者をやめる 読者になる 読者になる

gotin blog

Whatever gotin wanna write

無名関数と実行効率の話へのチャレンジ

function style2prop(str)
{
    return str.replace(/-[a-z]/g, function(str) {
        return str.charAt(1).toUpperCase();
    });
}

「無名関数ってこういうところに使うんですね」と太郎君が感心していると、そこにちょうど通りかかったのが、Javascriptの実行効率に関してものすごくうるさいことで知られる三郎君。「なになに?見せて」という三郎君に、コードを見せると、

「これ、実行効率上少しオーバーヘッドがあるよね。内部関数を隠蔽したいならもっと良い方法があるよ」と言います。

Life is beautiful: Javascriptクイズ(中級者向け):無名関数と実行効率の話

いくつか試してみた。

function style2prop(str){
    return str.replace(/-[a-z]/g, function(str) {
        return str.charAt(1).toUpperCase();
    });
}

function style2prop2(str){
  return str.replace(/-[a-z]/g, f);
  function f(s){
    return s.charAt(1).toUpperCase();
  }
}

var style2prop3 = (function(){
  var f = function(s){
    return s.charAt(1).toUpperCase();
  };
  return function(str){str.replace(/-[a-z]/g, f)};
})();

var style2prop4 = (function(){
  function f(s){
    return s.charAt(1).toUpperCase();
  }
  return function(str){str.replace(/-[a-z]/g, f)};
})();

function style2prop5(str){
  return str.replace(/-[a-z]/g, ff);
}
function ff(s){
  return s.charAt(1).toUpperCase();
}

function style2prop6(str){
  return str.replace(/-[a-z]/g, arguments.callee.f);
}
style2prop6.f = function(s){
  return s.charAt(1).toUpperCase();
}


Function.prototype.time = function(){
  var s = new Date().getTime();
  for(var i=0;i<10000;i++){
    var result = this.apply(null, arguments);
  }
  console.log("time:" + ((new Date().getTime() - s)/10000));
  return result;
}


style2prop.time("hoge-hogehoge");  //0.0186
style2prop2.time("hoge-hogehoge"); //0.0188
style2prop3.time("hoge-hogehoge"); //0.0223
style2prop4.time("hoge-hogehoge")/ //0.0223
style2prop5.time("hoge-hogehoge"); //0.0171
style2prop6.time("hoge-hogehoge"); //0.0233

あらぁ。
結局内部関数を使いつつ速度をもっと速くする方法はわからず。。

追記

id:brazilさんに教えてもらった方法も計測。

function style2prop(str){
  return str.replace(/-[a-z]/g, function(str) {
    return str.charAt(1).toUpperCase();
  });
}

function style2prop_global(str){
  return str.replace(/-[a-z]/g, ff);
}
function ff(s){
  return s.charAt(1).toUpperCase();
}

function style2prop_property(str){
  return str.replace(/-[a-z]/g, arguments.callee.f);
}
style2prop_property.f = function(s){
  return s.charAt(1).toUpperCase();
}

// ↓教えてもらった方法
function style2prop_initialize(str){
  function f (s){
    return s.charAt(1).toUpperCase();
  }
  return (style2prop_initialize = function(str){
    return str.replace(/-[a-z]/g, f);
  }).apply(null, arguments);
}

with(DEF()){
  // 計測結果                       ↓お。速い
  print(test()); // 290, 280, 357 ,285      by spidermonkey

  function test(){
    var TIMES= 10000;
    var timer = new Timer();
    var target = "hoge-hogehoge";
    timer.start();
    for(var i=0;i<TIMES;i++){
      style2prop(target);
    }
    timer.stop();

    timer.start();
    for(var i=0;i<TIMES;i++){
      style2prop_global(target);
    }
    timer.stop();

    timer.start();
    for(var i=0;i<TIMES;i++){
      style2prop_property(target);
    }
    timer.stop();

    timer.start();
    for(var i=0;i<TIMES;i++){
      style2prop_initialize(target);
    }
    timer.stop();

    return timer.times;
  }
}

function DEF(){
  function Timer(){}
  Timer.prototype = {
    index:0,
    times:[],
    start:function(i){
      this.times[this.index] = new Date().getTime();
    },
    stop:function(i){
      var time = new Date().getTime();
      var s = this.times[this.index];
      this.times[this.index] =  time - s;
      this.index++;
    }
  };
  return {Timer:Timer};
}

計測方法を変えたのは、関数定義を渡して実行する今までの計測方法だとstyle2prop_initializeの良さがなくなるから(毎回関数定義を作ることになるから)。
結果グローバルの関数を参照する方法の次に、最初の呼び出しのときに関数定義を作るstyle2prop_initializeの方法が速いことが分かりました。

それと、この計測をやっててわかったことを次の日記に書いておきます。