Ubuntu16.04の輻輳制御はデフォルトとBBRでどのくらい速度が違うのか比較

前置き

デフォルト設定のサーバとBBR設定済みのサーバを用意して、クライアントからサーバへアクセスしてファイルをダウンロードした時のダウンロード速度の比較です。
実際に使いそうな環境でBBRに変えた場合に、pingの違うサーバでどの程度の効果があるのか確認したかったので試してみました。
とはいえ環境や回線の混み具合で結果が大幅に変わるガバガバな測定方法なので、決して当てにしてはいけない数値です。
あと測定方法は以下のPowerShellスクリプトを参考にしたのですが、これも信用できるかよく分かっていないです。
PowerShell: Measuring Download Speeds – Trevor Sullivan’s Tech Room

測定環境

サーバ

  • OS : Ubuntu 16.04.4
  • kernel : 4.13.0-36-generic
  • 場所 : 東京、シアトル、パリ (VultrのVPSをそれぞれの場所のデータセンターに構築)
  • HTTPサーバ : Apache2
  • sysctlの設定
    設定 デフォルト BBR
    net.core.default_qdisc pfifo_fast fq
    net.ipv4.tcp_congestion_control cubic bbr

クライアント

  • OS : Windows10
  • 場所 : 日本国内
  • 回線 : FLETS光

測定方法

  • 東京、シアトル、パリの3箇所に1GBのテストデータを置いたHTTPアクセス可能なサーバを構築。
  • サーバの輻輳制御アルゴリズムはデフォルト(cubic)とBBRの2つ。
  • クライアントからテストデータをダウンロードした際のダウンロードファイルのサイズを測定。

測定結果

東京に構築したサーバ

  • Ping平均 : 6ms
デフォルト(cubic) BBR
平均速度(3回試行) 19.12MB/sec 65.45MB/sec
平均完了時間(3回試行) 50秒 15秒

シアトルに構築したサーバ

  • Ping平均 : 99ms
デフォルト(cubic) BBR
平均速度(3回試行) 6.01MB/sec 22.62MB/sec
平均完了時間(3回試行) 2分38秒 42秒

パリに構築したサーバ

  • Ping平均 : 231ms
デフォルト(cubic) BBR
平均速度(3回試行) 2.93MB/sec 9.81MB/sec
平均完了時間(3回試行) 5分25秒 1分38秒

所感

実施前は速くなったとしても数%程度だと思っていたので、正直なところ測定ミスを疑って同じ手順で2回やってみています。
BBRの速度はグラフで見ると天井に張り付くような形で推移しており、特にpingの大きいパリの結果で顕著です。回線速度の上限までうまく使っているということなのかな?

どうにしろ、速度が欲しくてOSをチューニングする場合には、試すべき一手と言えそうです。


Ubuntu 16.04に新しいカーネルをインストールしてBBRを使用する

BBRとは

Linuxカーネルの4.9からGoogleが開発したBBRというアルゴリズムを通信に使えるらしい。
古いアルゴリズムと比べてTCPの転送速度が早くなるらしい。なら使わなきゃ損でしょ。
でもUbuntu 16.04のデフォルトのカーネルは古くて対応してないので、新しいものを入れてBBRの設定もする手順を残しておく。
Ubuntu 18.04ならカーネルが4.15らしいので設定するだけで使えそう。もしかするとデフォルトでオンになるかもしれないけど。

確認環境

  • OS : Ubuntu 16.04.3

新しいカーネルのインストール

カーネルパッケージの一覧を確認

apt-cache search linux-generic

以下のように表示される

~~~
linux-image-4.13.0-32-generic - Linux kernel image for version 4.13.0 on 64 bit x86 SMP
~~~

特にこだわりはないので例では確認時点の最新版(linux-image-4.13.0-32-generic)をインストール

sudo apt install linux-image-4.13.0-32-generic

BBRの有効化

「/etc/sysctl.d/10-custom-kernel-bbr.conf」ファイルを作成

sudo vi /etc/sysctl.d/10-custom-kernel-bbr.conf
#以下を記述して保存
---
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
---

カーネルの有効化も兼ねて再起動

sudo reboot

BBRの設定を確認

sysctl net.core.default_qdisc
sysctl net.ipv4.tcp_congestion_control

---
#こうなる
$ sysctl net.core.default_qdisc
net.core.default_qdisc = fq
$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = bbr
---

これで設定完了。

時間ができたら実測してみた値とかまとめたい。


WallpaperEngineの壁紙をUnityのWebGLで作るためのメモ

WallpaperEngineの壁紙をUnityで作成する場合、Windowsの実行形式(.exe)で出力すれば手軽に作成することができます。
しかし、Web形式(HTML)以外ではWallpaperEngineで壁紙を設定する際のプロパティを作成者で任意に作成できないため、ユーザにパラメータ変更を委ねたいときに面倒です。
UnityにはWebGLで出力する機能もあり、そちらをWallpaperEngineに使用する方法を調べたのですが、まとまった情報がなかったのでメモしておきます。

以下はUnity Ver 2017.1 で確認した内容です。

WebGLテンプレート

WallpaperEngineで表示した時に表示を最大化し、必要なJavascript外部ファイルを読み込むためHTMLのテンプレートを設定します。

参考: Unity – Manual: Using WebGL Templates

配置 : Assets > WebGLTemplates > (任意の名前) > index.html

<!DOCTYPE html>
<html lang="en-us">

  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
    <script src="PropertyScript.js"></script>
    <script src="%UNITY_WEBGL_LOADER_URL%"></script>
    <script>
      var gameInstance = UnityLoader.instantiate("gameContainer", "%UNITY_WEBGL_BUILD_URL%");
    </script>
    <style>
      body {
        overflow: hidden;
        margin: 0;
        padding: 0;
        background-color: black;
      }
    </style>
  </head>
  <body>
    <div id="gameContainer" style="width: 100%; height: 100%; position: absolute;"></div>
  </body>
</html>

Edit > Project Settings > Playerから追加したテンプレートを選択しておくこと。

WallpaperEngineで読み込むプロジェクトファイル

プロパティの値が定義されています。
プロパティ部分は、WallpaperEngineの作成ガイドにあるテンプレと同様です。

参考: Advanced: Web user customization

配置 : Assets > WebGLTemplates > (任意の名前) > project.json

{
    "file" : "index.html",
    "preview" : "prev.png",
    "general" : 
    {
        "properties" : 
        {
            "customcolor" : 
            {
                "text" : "User accent",
                "type" : "color",
                "value" : "0.2 0.8 1"
            },
            "schemecolor" : 
            {
                "text" : "Scheme color",
                "type" : "color",
                "value" : "0.7 0.2 0.1"
            },
            "custombool" : 
            {
                "text" : "User bool",
                "type" : "bool",
                "value" : false
            },
            "customint" :
            {
                "text" : "User slider",
                "type" : "slider",
                "value" : 50,
                "min" : 0,
                "max" : 100
            },
            "customcombo" :
            {
                "text" : "User combo",
                "type" : "combo",
                "value" : 1,
                "options" :
                [
                    {
                        "value" : 1,
                        "label" : "First"
                    },
                    {
                        "value" : 2,
                        "label" : "Second"
                    },
                    {
                        "value" : 3,
                        "label" : "Third"
                    }
                ]
            }
        }
    },
    "title" : "WebGL Template",
    "type" : "web"
}

プロパティを受け取るスクリプト

WallpaperEngineのプロパティはブラウザ?のJavascriptに用意した特定の関数が呼び出されることで受け渡しをするため、Unityのエディタとは別にスクリプトを用意します。
後述する「Javascript->C#」間の値の受け渡しの都合から、格納する値をINT型にしています。

参考: Advanced: Web user customization

配置 : Assets > WebGLTemplates > (任意の名前) > PropertyScript.js

var WALLPAPER_ENGIN_PROPERTY = WALLPAPER_ENGIN_PROPERTY || {};
window.wallpaperPropertyListener = {
    applyUserProperties: function(properties) {
        // Read scheme color
        if (properties.schemecolor) {
            WALLPAPER_ENGIN_PROPERTY.schemecolor = PropertyColorToInt(properties.schemecolor.value);
        }

        // Read custom color
        if (properties.customcolor) {
            WALLPAPER_ENGIN_PROPERTY.customcolor = PropertyColorToInt(properties.customcolor.value);
        }

        // Read custom boolean
        if (properties.custombool) {
            WALLPAPER_ENGIN_PROPERTY.custombool = PropertyBoolToInt(properties.custombool.value);
        }

        // Read custom slider
        if (properties.customint) {
            WALLPAPER_ENGIN_PROPERTY.customint = properties.customint.value;
        }

        // Read custom combo box
        if (properties.customcombo) {
            WALLPAPER_ENGIN_PROPERTY.customcombo = properties.customcombo.value;
        }

        function PropertyColorToInt(colorValue){
            var Colors = colorValue.split(' ');
            colorInt = Math.ceil(Colors[0] * 255) * 1000 * 1000;
            colorInt += Math.ceil(Colors[1] * 255) * 1000;
            colorInt += Math.ceil(Colors[2] * 255);
            return colorInt;
        }

        function PropertyBoolToInt(boolValue){
            return Number(boolValue);
        }
    }
};

WebGLからブラウザのJavascript関数を呼び出すプラグイン

WebGLからブラウザのJavascriptの関数を呼び出せるよう、プラグインとしてスクリプトファイルを作成します。

参考: Unity – マニュアル: WebGL: ブラウザのスクリプトと通信を行う

配置 : Assets > Plugins > WebGL > PropertyPlugin.jslib

var PropertyPlugin = {
    GetSchemeColor: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.schemecolor;
    },
    GetCustomColor: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.customcolor;
    },
    GetCustomBool: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.custombool;
    },
    GetCustomInt: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.customint;
    },
    GetCustomCombo: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.customcombo;
    }
};

mergeInto(LibraryManager.library, PropertyPlugin);

WebGLからブラウザのJavascript関数を呼び出すスクリプト

Asset内の任意の場所に保存し、適当なオブジェクトでコンポーネントとして呼び出し。
マニュアルにINT型とString型を呼び出す方法しかなかったため、全てINTでやり取りしています。
以下の例ではUI>Textオブジェクトにプロパティで取得した値を反映しています。

参考: Unity – マニュアル: WebGL: ブラウザのスクリプトと通信を行う

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using System;

public class PropertyTestScript : MonoBehaviour {
    public Text[] TextObj;

    [DllImport("__Internal")]
    private static extern int GetSchemeColor();

    [DllImport("__Internal")]
    private static extern int GetCustomColor();

    [DllImport("__Internal")]
    private static extern int GetCustomBool();

    [DllImport("__Internal")]
    private static extern int GetCustomInt();

    [DllImport("__Internal")]
    private static extern int GetCustomCombo();

    private static Color SchemecolorIntToColor(int schemecolorInt)
    {
        int red = schemecolorInt / 1000 / 1000;
        int green = schemecolorInt / 1000 % 1000;
        int blue = schemecolorInt % 1000;
        return rgb(red, green, blue);
    }

    private static Color rgb(int red, int green, int blue)
    {
        return new Color(red / 255f, green / 255f, blue / 255f);
    }

    private static bool SchemeboolIntToBool(int schemeboolInt)
    {
        return Convert.ToBoolean(schemeboolInt);
    }

    void Start () {
    }

    void Update () {
        TextObj[0].color = SchemecolorIntToColor(GetSchemeColor());
        TextObj[1].color = SchemecolorIntToColor(GetCustomColor());
        TextObj[2].text = "GetCustomBool : " + SchemeboolIntToBool(GetCustomBool()).ToString();
        TextObj[3].text = "GetCustomInt : " + GetCustomInt().ToString();
        TextObj[4].text = "GetCustomCombo : " + GetCustomCombo().ToString();
    }
}