TwitterFoxのパフォーマンス改善

(2009/06/26追記)TwitterFoxのバージョンアップによってこの問題は解消しました。1.8.2以降であれば下記の修正は必要ありません。

※以下の内容は、私の中途半端な知識に基づいた適当な改造となっております。この内容をそのまま適用した場合、何らかの問題が発生するかもしれません。そのあたりは自己責任ということでお願い致します。
※間違っている部分へのツッコミは大歓迎です。というか、是非よろしくお願い致します。

前置き

TwitterFox、便利ですよね。私みたいな、それほどヘビーでもないけど、完全にライトなわけでもない…という中途半端なついたったー(ついったらー?)には、非常にお手軽でありがたいツールです。

そのTwitterFoxが2009/06/11に1.8にバージョンアップし、見た目的にも内部的にも大幅に変更が加えられました。*1

基本的には歓迎できる改良点ばかりなのですが、一つだけ、どうしても気になる点が…。

タイムラインの取得時に、今まで感じていなかった「引っかかり」のようなものを感じるようになってしまったんですね。私はよくニコニコ動画を見ているのですが、TwitterFoxが新しいステータスを受信するたびに動画再生が一瞬停止してしまったりして、正直ちょっとストレスを感じるようになってしまいました。

この機会に他のツールを探してみる、という選択肢もあったのですが、それなりにTwitterFoxを気に入っていますし、ボトルネックを探して自分で解決する方法をとってみることにしました。

原因

ここまでで既に前置きが長くなってしまいましたので…途中経過はばっさりと省略して。

結論から言うと、DB(SQLiteファイル)への書き込み処理がボトルネックとなっていた模様です。

components/nsTwitterFox.js の683行目から始まる TwitterNotifier#retrieveTimeline 、及び同ファイルの前半部分に定義されている User , Status, DirectMessage 各クラスの insertIntoDB メソッドを追ってみるとわかるのですが、どうやら

  • ステータスが1つ更新される度に3つのINSERT文が実行される
  • それらはトランザクションに纏められておらず、バラバラに実行される

という状態になっているらしいんですね。

トランザクションの外にあるINSERT文は、それ自体が一つの小さなトランザクションとして処理されますので、例えば1度の更新で20のステータスを受信した場合、20*3=60回ものトランザクションが実行されることになります。

さすがにそれでは重くもなりますよね…。

対応

ということで、やっつけ対応ですが、 TwitterNotifier#retrieveTimeline の中で msg.insertIntoDB を呼び出しているforループ全体を db.exec("BEGIN TRANSACTION");db.exec("COMMIT TRANSACTION"); で囲ってやります。

こんな感じ。

  retrieveTimeline: function(obj, req, method) {
    if (obj) {
      // Added 'unread' flag if the message is new in any of stored messages
      this.log("Get " + obj.length + " " + method);
      db.exec("BEGIN TRANSACTION"); // ← 追加
      for (var i in obj) {

        var msg;
        var result;

        if (method == "messages") {
          msg = new DirectMessage(obj[i]);
          result = msg.insertIntoDB(this._accounts[this._user].id);
        }
        else {
          msg = new Status(obj[i], method);
          result = msg.insertIntoDB(this._accounts[this._user].id);
        }
        msg.type = method;
        if (result) {
          this._unread[method].push(msg.id);
          this._newMessages.push(msg);
        }
      }
      db.exec("COMMIT TRANSACTION"); // ← 追加
    }
  },

これで何個ステータスを受信しようが1つのトランザクションで纏めて処理されますので、処理にかかる時間は最小限に抑えられるはずです。

実際に私の環境では、この変更を加えて以降、今のところ「引っかかり」は感じていません。やったねたえちゃん!

その他の変更

私版TwitterFoxでは、他にも個人的な好みにより、以下の修正が加えてあります。

components/nsTwitterFox.js の14行目

var MAX_STORED_MESSAGES = 40;

var MAX_STORED_MESSAGES = 100;

に変更。

更に、同じく components/nsTwitterFox.js の555〜556行目

      var count = this._unread[type].length;
      if (count < 20) count = 20;

      var count = this._unread[type].length + 20;
      if (count < MAX_STORED_MESSAGES) count = MAX_STORED_MESSAGES;

に変更。

見ていただければわかるかと思いますが、通常は最大100件、未読数が80件以上ある場合は未読数+20件まで一度に表示できるように変更してあります。

このあたりはタイムラインの速さによって色々調整してみるのもいいかもしれません。

パッチ

ここまでの変更をパッチに纏めると、こんな感じになります。

diff -u -r twitterfox-1.8.1-fx/components/nsTwitterFox.js twitternotifier@naan.net/components/nsTwitterFox.js
--- twitterfox-1.8.1-fx/components/nsTwitterFox.js      2009-06-10 09:40:18.000000000 +0900
+++ twitternotifier@naan.net/components/nsTwitterFox.js 2009-06-13 15:42:14.000000000 +0900
@@ -11,7 +11,7 @@
 var CLASS_NAME = "Twitter Notifier";
 var CONTRACT_ID = "@naan.net/twitternotifier;1";
 
-var MAX_STORED_MESSAGES = 40;
+var MAX_STORED_MESSAGES = 100;
 
 var TWEET_FRIENDS   = 0;
 var TWEET_MENTIONS  = 1;
@@ -552,8 +552,8 @@
     var type = obj.type;
     if (this._accounts[this._user]) {
       var ret = {};
-      var count = this._unread[type].length;
-      if (count < 20) count = 20;
+      var count = this._unread[type].length + 20;
+      if (count < MAX_STORED_MESSAGES) count = MAX_STORED_MESSAGES;
       if (type == "messages") {
         ret.msgs = this.restoreMessagesFromDB(this._accounts[this._user].id, count);
       }
@@ -684,6 +684,7 @@
     if (obj) {
       // Added 'unread' flag if the message is new in any of stored messages
       this.log("Get " + obj.length + " " + method);
+      db.exec("BEGIN TRANSACTION");
       for (var i in obj) {
 
         var msg;
@@ -703,6 +704,7 @@
           this._newMessages.push(msg);
         }
       }
+      db.exec("COMMIT TRANSACTION");
     }
   },
 

このパッチを %APPDATA%\Mozilla\Firefox\Profiles\(プロファイル名)\extensions\twitternotifier@naan.net\components\nsTwitterFox.js に対して適用すればOKです。

快適なTwitterFoxライフをお楽しみ下さい!

ついでに

1.8.1になってデフォルトフォントサイズが小さくなったようですので、見た目が大きく変わってちょっとギョっとするかもしれませんが、普通に設定画面*2からフォントサイズの変更ができますので、そこで大きめに設定してやればOKです。1.8以前はフォントサイズ12相当だった模様なので、私は12に設定しておきました。

*1:翌日06/12に早速1.8.1へのマイナーバージョンアップがありましたが、この記事で述べている範囲のことは1.8.1でもそのまま適用できます。

*2:TwitterFoxのアイコンを右クリック