ns-3を使ったLTE通信のシミュレーション

ns-3とはオープンソースの離散事象ネットワークシミュレータです。ns-3を使ってLTE通信のシミュレーションを行っていた時、TCPを使うとスループットが出ない現象がありました。その原因と解決方法について書いていきます。

 

要約

本記事の要約は以下の通りです。

  • LTEモジュールのサンプルスクリプトを動かす
  • スループットの表示機能を追加する
  • サンプルスクリプトを改造してUDPからTCPに変更したところスループットが出なくなる
  • RLCレイヤの送信バッファが溢れ、パケットがドロップして、輻輳制御が働いていたことが原因

ns-3について

本記事で扱うns-3のバージョンは現在の最新版であるns-3.28です。ns-3のインストール方法につきましてはGetting Started — Tutorialまたはns-3.26で始めるネットワークシミュレーションを参照ください。

ns-3に付属しているLTEモジュールのドキュメントは以下の通りです。

LTEモジュールのサンプルスクリプト

まず、LTEの通信を行うサンプルスクリプトを動かしてみます。サンプルスクリプトのトラフィックは以下のようになっています (EPCは省略しています)。

矢印はトラフィックを表しています。各トラフィックは100ミリ秒おきにUDPパケットを1個送信しています。また、各eNBとUEのペアの距離は0です。

それではサンプルスクリプトを./scratch/にコピーして動かしてみます。

$ cd ~/ns-allinone-3.28/ns-3.28
$ cp ./src/lte/examples/lena-simple-epc.cc ./scratch/lte-example.cc
$ ./waf --run lte-example

eNBとUEの無線区間に関するログファイルがいくつかファイル出力されていますが、スループットは表示されません。

以降では、./scratch/lte-example.ccに手を加えていきます。

スループットの表示

lte-example.cc(以降、スクリプト)にコードを追加して、スループットを表示します。スループットの表示にはいくつか方法がありますが、今回はns-3のモジュールのFlow Monitorを使います。

まずはFlow Monitorをインクルードします。

#include "ns3/flow-monitor-helper.h"
#include "ns3/flow-monitor-module.h"

スクリプト内のSimulator::Stop(Seconds(simTime));の前に以下のコードを追加します。

  FlowMonitorHelper flowmon;
  Ptr<FlowMonitor> monitor = flowmon.InstallAll ();

  Simulator::Stop(Seconds(simTime));

Simulator::Run();の後に以下のコードを追加します。

  Simulator::Run();

  // Print per flow statistics
  monitor->CheckForLostPackets ();
  Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowmon.GetClassifier ());
  FlowMonitor::FlowStatsContainer stats = monitor->GetFlowStats ();
  for (std::map<FlowId, FlowMonitor::FlowStats>::const_iterator i = stats.begin (); i != stats.end (); ++i)
    {
      Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow (i->first);
      std::cout << "Flow " << i->first << " (" << t.sourceAddress << " -> " << t.destinationAddress << ")\n";
      std::cout << "  Tx Packets: " << i->second.txPackets << "\n";
      std::cout << "  Tx Bytes:   " << i->second.txBytes << "\n";
      std::cout << "  TxOffered:  " << i->second.txBytes * 8.0 / (simTime) / 1000 / 1000  << " Mbps\n";
      std::cout << "  Rx Packets: " << i->second.rxPackets << "\n";
      std::cout << "  Rx Bytes:   " << i->second.rxBytes << "\n";
      std::cout << "  Throughput: " << i->second.rxBytes * 8.0 / (simTime) / 1000 / 1000  << " Mbps\n";
    }

動かしてみます。

$ ./waf --run lte-example
--- (省略) ---
Flow 1 (1.0.0.2 -> 7.0.0.2)
  Tx Packets: 11
  Tx Bytes:   11572
  TxOffered:  0.08416 Mbps
  Rx Packets: 10
  Rx Bytes:   10520
  Throughput: 0.0765091 Mbps

--- (省略) ---
Flow 6 (7.0.0.3 -> 1.0.0.2)
  Tx Packets: 11
  Tx Bytes:   11572
  TxOffered:  0.08416 Mbps
  Rx Packets: 10
  Rx Bytes:   10520
  Throughput: 0.0765091 Mbps

1.0.0.2はRemoteHostです。7.0.0.2、7.0.0.3はそれぞれUE1とUE2です。ここでは省略していますが、実際の出力では、3つのトラフィックを持つUEが2つがあることが確認できます。Flow MonitorはIPレイヤを監視して、送受信パケット数を取得しています。各ノードが100ミリ秒おきに1パケットを送信していて、シミュレーションの実行時間が1.1秒なので、11パケット送信しています。受信パケット数が10の理由は、クライアントとサーバの起動時間が同時のため、サーバが1個目のパケットを取りこぼしているからです。

以下のようにしてクライアントの起動時間を遅らせると、受信パケット数が11になります。

  serverApps.Start (Seconds (0.01));
  clientApps.Start (Seconds (0.03));

TCPアプリケーション

トラフィック種別とトポロジを変更してみます。サンプルスクリプトでは100ミリ秒おきに1個のUDPパケットを送信していました。これを変更して、送信側の送信バッファがいっぱいになるまでTCPパケットを送信し続け、バッファに空きが出たら送信を再開するようにします。ns-3のモジュールのBulkSendApplicationを使います。

また、トラフィックの見やすさのために、eNBとUEの数を1つに減らし、RetoteHostからUEへの一方向のトラフィックにします。図に示すと以下のようになります。eNBとUEの距離は0になります。

eNBとUEの個数を1つにします。

  uint16_t numberOfNodes = 1;

トラフィックの設定を変更します。スクリプト中の”// Install and start applications on UEs and remote host”から”serverApps.Start (Seconds (0.01));”の間を以下のように変更します。

  // Install and start applications on UEs and remote host
  uint16_t dlPort = 1234;
  ApplicationContainer clientApps;
  ApplicationContainer serverApps;
  for (uint32_t u = 0; u < ueNodes.GetN (); ++u)
    {
      PacketSinkHelper dlPacketSinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), dlPort));
      serverApps.Add (dlPacketSinkHelper.Install (ueNodes.Get(u)));
      BulkSendHelper dlClient ("ns3::TcpSocketFactory", InetSocketAddress (ueIpIface.GetAddress (u), dlPort));
      clientApps.Add (dlClient.Install (remoteHost));
    }
    serverApps.Start (Seconds (0.01));

サーバとクライアントの起動が同時になると、SYNパケットが通らずに通信が行われないので、起動時間をずらします。

  serverApps.Start (Seconds (0.01)); 
  clientApps.Start (Seconds (0.1)); 

スクリプトを実行します。シミュレーション時間は5.1秒に設定しています。

$ ./waf --run "lte-example simTime=5.1"
Flow 1 (1.0.0.2 -> 7.0.0.2)
  Tx Packets: 1632
  Tx Bytes:   958548
  TxOffered:  1.5036 Mbps
  Rx Packets: 1613
  Rx Bytes:   947376
  Throughput: 1.48608 Mbps
Flow 2 (7.0.0.2 -> 1.0.0.2)
  Tx Packets: 871
  Tx Bytes:   46320
  TxOffered:  0.0726588 Mbps
  Rx Packets: 864
  Rx Bytes:   45956
  Throughput: 0.0720878 Mbps

※ 1.0.0.2から7.0.0.2への通信がRemoteHostからUEへのアプリケーションによる通信で、逆方向の通信はAckになります。

LTEモジュールのリソースブロック(RB)のデフォルト値は25になっているため、最大18Mbps程度のスループットが期待できますが、出力結果を見ると出ていません。

原因は以下の2点です。

  • RLCレイヤの送信バッファの溢れ
  • RemoteHostとEPC間のディレイ

RLCレイヤの送信バッファサイズを変更します。”// Command line arguments”の直前に以下のコードを追加します。

  Config::SetDefault("ns3::LteRlcUm::MaxTxBufferSize", UintegerValue(1024 * 1024));
  
  // Command line arguments

RemoteHostとEPC間のディレイを変更します。

  p2ph.SetChannelAttribute ("Delay", TimeValue (Seconds (0.001)));

スクリプトを実行します。

$ ./waf --run "simple-epc --simTime=5.1"
Flow 1 (1.0.0.2 -> 7.0.0.2)
  Tx Packets: 18923
  Tx Bytes:   10688960
  TxOffered:  16.767 Mbps
  Rx Packets: 18687
  Rx Bytes:   10555856
  Throughput: 16.5582 Mbps
Flow 2 (7.0.0.2 -> 1.0.0.2)
  Tx Packets: 9344
  Tx Bytes:   485892
  TxOffered:  0.762184 Mbps
  Rx Packets: 9334
  Rx Bytes:   485372
  Throughput: 0.761368 Mbps

スループットが出るようになりました。

最後に、帯域幅を変更してみます。ここではRB数を25から100に変更します。帯域幅でいうと5MHzから20MHzになります。RB数が100の場合、最大72Mbps程度のスループットが期待されます。

以下のようにコードを追加します。

  Config::SetDefault("ns3::LteEnbNetDevice::UlBandwidth", UintegerValue(100));
  Config::SetDefault("ns3::LteEnbNetDevice::DlBandwidth", UintegerValue(100));
  Config::SetDefault("ns3::LteRlcUm::MaxTxBufferSize", UintegerValue(1024 * 1024));

スクリプトを実行します。

$ ./waf --run "simple-epc --simTime=5.1"
Flow 1 (1.0.0.2 -> 7.0.0.2)
  Tx Packets: 79477
  Tx Bytes:   44841416
  TxOffered:  70.3395 Mbps
  Rx Packets: 79305
  Rx Bytes:   44744408
  Throughput: 70.1873 Mbps
Flow 2 (7.0.0.2 -> 1.0.0.2)
  Tx Packets: 39653
  Tx Bytes:   2061960
  TxOffered:  3.23445 Mbps
  Rx Packets: 39611
  Rx Bytes:   2059776
  Throughput: 3.23102 Mbps

70Mbps程度の速度が出ていることが確認できます。

LTEでは、1つのUEが1つのeNBを独占する形、かつ通信品質が良好の場合、およそ70Mbps出ることがわかります。

おわりに

LTEのシミュレーションでTCPのスループットが出ない現象について紹介しました。期待した数値が出ないときには、LTE固有のレイヤでのバッファ溢れや、ノード間の遅延が原因であったりします。LTEモジュールの解析には各レイヤを注意深く見る必要があります。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*