DNSプリフェッチでSafariを高速化


 まず始めに、ChromeDNSによる名前解決結果のキャッシュと「DNSプリフェッチ」技術を用いて、名前解決処理にかかる時間を短縮している。DNSプリフェッチとは、Webページ上にあるリンク先ホスト名を先読みし、あらかじめ名前解決を行って結果をキャッシュしておく機能だ。Chromeでは、このDNSプリフェッチ機能が初期設定で有効になっている。

そんなに速くなるのならと思って、MacSafariでも、UserJSを使ってDNSプリフェッチを実現してみました。


任意のWebページのロード完了後3秒経ったら、GreaseKitを使ってJavaScriptを走らせ、ページ内の全てのリンクのドメイン一覧を取得します。それをローカルで動くDNSPrefetchサーバに送信し、サーバ側であらかじめDNS解決を行うことで自分のMac内のDNSキャッシュを作っておくというものです。
JavaScriptだけで名前解決できればもうちょっと簡単になるのですが良い方法が解らない)


効果は、仕掛けが大げさな割には、まあ多少は早いかな?という感じ。
Googleの検索結果からのジャンプだと特に別ドメインに移動する可能性が高い分、快適になる気がします。

まあChromeも他に工夫はいろいろしているでしょうし。参考:Chromeはなぜ速いのか − @IT



# どっちかというと、GreaseKit入れたついでにoAutopagerize入れた方が快適さup度は大かも。
## そしてAutopagerizeなしでは生きられない体に。

動かし方:

  1. 以下のUserJSとperlスクリプト(DNSPrefetchサーバ)をファイルに保存。
  2. SIMBLをインストールしていない場合はインストール(GreaseKitが使用)。
  3. GreaseKitを入れていない場合はインストール。
  4. perlモジュールのHTTP::DaemonやHTTP::Statusが無い場合はCPANなどでインストール。
  5. ターミナル上で DNSPrefetch.pl を起動 (perl DNSPrefetch.pl )。& + disown でバックグラウンドに回したり端末から切り離しても良いです。
  6. UserJSをSafariで開き、GreaseKitに追加。
  7. アンインストールする時はGreaseKitのManage Scripts...から。

コード

  • DNSPrefetcher.user.js
// ==UserScript==
// @name           DNSPrefetcher
// @namespace      http://d.hatena.ne.jp/NeoCat/
// @description    Prefetch DNS on link in the current web pages to enfast the browsing. Support GreaseKit(Safari/WebKit).
// @include        http://*
// @include        https://*
// @exclude       http://localhost:50129/*
// ==/UserScript==

(function(){
	var prefetchDNS = function(domains) {
		var pff = document.createElement("iframe");
		pff.style.display = "none";
		pff.src = "http://localhost:50129/fetch?" + domains;
		pff.onload = function(){ pff.parentNode.removeChild(pff); }
		document.body.appendChild(pff);
	}

	setTimeout(function(){
		var links = document.getElementsByTagName("a");
		var d_hash = new Array;
		var domains = "";
		for (var x = 0; x < links.length; x++) {
			if (links[x].href && links[x].href.match(/^https?:\/\/([^\/]+)/)) {
				var domain = RegExp.$1;
				domain = domain.replace(/^.*@/,'').replace(/:.*$/,'');
				if (domain && domain != "" && !d_hash[domain]) {
					d_hash[domain] = true;
					if (domains != "") domains += ","
					domains += domain;
				}
			}
		}
		if (domains.length > 0) prefetchDNS(domains);
	}, 3000);
})();
  • DNSPrefetch.pl
#!/usr/bin/perl
use strict;
use HTTP::Daemon;
use HTTP::Status;

my $d = HTTP::Daemon->new(LocalAddr => '127.0.0.1', LocalPort => 50129) or die $!;
while (my $c = $d->accept) {
	while (my $r = $c->get_request) {
		if ($r->method eq 'GET') {
			my $q = $r->url->query;
			$c->force_last_request();
			print "*request : $q\n";
			my @domains = split ",", $q;
			for (@domains) {
				print "fetching $_\n";
				gethostbyname($_) || print "    failed!\n";
			}
			my $h = HTTP::Headers->new('Content-Type' => 'text/plain');
			my $res = HTTP::Response->new(200, 'OK', $h, '"OK"'.$/);
			$c->send_response($res);
		} else {
			$c->send_error(RC_FORBIDDEN)
		}
	}
}