[misc.] JJUG CCC 2017 Fall

先週の土曜日、西新宿で開催された、JJUG CCC 2017 Fallで、一コマ(20分枠)を頂戴できたので、AOT(Ahead of Time) コンパイルについてお話することができました。セッションを聴講くださったみなさま、JJUGのみなさま、ありがとうございました。ニッチな内容ゆえに満席になるとは思っていなかったので、非常にびっくりしました。

実際にコードをビルドして実行してみたり、パフォーマンスについてお話するには、明らかに20分では足りませんでしたね(45分枠にしておくんだった)。スライドは、一部を除きSlideShareにUpしています(抜いた一部のスライドは、聴講くださった方だけの「おみやげ」ということで)。



とにもかくにも、Java、特にHotSpotでAOTが利用できるようになっていることを知っていただき、「なんか面白そうだからやってみよう」と興味を持ってもらえたのであれば、今回のプレゼンテーションの目的は果たせたかな、と思っています。

Java 10では、Linux x64だけではなく、macOS、WIndowsでも利用できるようになる予定です。「試してみたい!」という方は、JDK 10 Early Accessビルドを入手して、試してみてください。なおWindowsの場合は、Visual Studioのamd64用link.exeが必要です。

macOS (High Sierra)

[Linux] Running Oracle Linux 5 applications in Oracle Cloud Infrastructure using lxc.

原文はこちら。
https://blogs.oracle.com/wim/running-oracle-linux-5-applications-in-oracle-cloud-infrastructure-using-lxc

Oracle Cloud Infrastructureのベアメタルサーバおよび仮想マシンはEFI対応のOSが必要で、お客様がインスタンスに展開するためにOracle Linux 6とOracle Linux 7イメージを提供しています。ほとんどのアプリケーションはこれらのOSバージョンで動作保証およびサポートされていますが、Oracle Linux 4や5を必要とするような古いアプリケーションをお持ちになる場合もあります。現時点では、これらのバージョンをネイティブインスタンスで実行することはできませんが、Oracle Linux 4やOracle Linux 5環境を持つLinux ContainersをOracle Linux上で実行することは可能です。
長年にわたり、Oracle Linuxでは lxc をサポートしてきました。
Oracle Linux containers
https://blogs.oracle.com/wim/oracle-linux-containers
lxc はシステムコンテナに最適で、OS環境全体(基本的には"start /bin/init")を呼び出すことができますが、それに対しDockerは、アプリケーションの起動、つまりアプリケーション・コンテナです。確かにアプリケーションとして /bin/init を実行することはできますが、 lxc はこのモデルに対して少々チューニングされているます。汎用的なlxcのドキュメントは以下にあります。
Oracle® LinuxAdministrator's Guide for Release 7
Linux Containers
https://docs.oracle.com/cd/E52668_01/E54669/html/ol7-containers.html
lxc はOracle Linux 6および7で完全にサポートされており、Oracle Linux 5はlxc上のコンテナOSとして完全にサポートされています。そのため、OCIのLinuxの古いバージョン上で古いアプリケーションを実行する必要のあるお客様にとっては、これは最適な選択肢です。
Oracle Cloud Infrastructureで lxc を使ってみるためには、
  • まずOSイメージとしてOracle Linux 7を使うベアメタルサーバもしくはVMインスタンスを作成
  • 仮想クラウドネットワークを作成
  • ブロック・ボリュームを作成
  • ブロック・ボリュームのインスタンスへのアタッチ
などの作業が必要で、こうした作業手順に詳しいものとします。あと、追加でVNIC周りでもう一つ仮定を置きます。最も簡単なネットワーク設定は、別々のセカンダリVNICを各コンテナに割り当て、このVNICをコンテナに渡すことです。簡単なチュートリアルは以下にあります。
Tutorial - Launching Your First Linux Instance
https://docs.us-phoenix-1.oraclecloud.com/Content/GSG/Reference/overviewworkflow.htm
作業手順を纏めると以下のようです。
  • コンパートメント、仮想クラウドネットワーク(VCN)、サブネットを作成
  • インスタンス(ベアメタルもしくはVM)を作成
  • コンテナをホストするブロック・ボリュームを作成
  • VNICを作成(コンテナあたり1個)
  • lxc のインストール
  • コンテナを置くブロックボリュームでファイルシステムを作成し、マウント
  • コンテナの作成
lxcをインストールするには、Oracle Linuxインスタンスでyumを使います。
# yum install lxc
...
Dependencies Resolved
================================================================================
 Package         Arch          Version                  Repository         Size
================================================================================
Updating:
 lxc             x86_64        1.1.5-2.0.9.el7          ol7_latest        231 k
Updating for dependencies:
 lxc-libs        x86_64        1.1.5-2.0.9.el7          ol7_latest        219 k
Transaction Summary
================================================================================
Upgrade  1 Package (+1 Dependent package)
Total download size: 450 k
Is this ok [y/d/N]:
lxcの最新バージョン(1.1.5-2.0.9 以上)であることを確認してください。
btrfsをコンテナファイルシステムとして利用するものとします。
ブロックボリュームを作成していれば、/dev/sdbとして確認できるはずです。
Tutorial - Launching Your First Linux Instance
Adding a Block Volume 
https://docs.us-phoenix-1.oraclecloud.com/Content/GSG/Tasks/addingstorage.htm
$ cat /proc/partitions
major minor  #blocks  name
   8        0   48838656 sda
   8        1     556988 sda1
   8        2    8420348 sda2
   8        3   39808260 sda3
   8       16  134217728 sdb
fdiskでパーティションを作成しますが、今回は簡単のためにボリューム全体を使って1個のパーティションを作成します。
$ fdisk /dev/sdb
Enter n (new partition), p (primary partition) 1 (first  partition on new volume) and hit enter twice if you want to use the  entire Block Volume.
Enter w to write the partition table out to disk.
以下のような表示になることでしょう。
$ cat /proc/partitions
major minor  #blocks  name
   8        0   48838656 sda
   8        1     556988 sda1
   8        2    8420348 sda2
   8        3   39808260 sda3
   8       16  134217728 sdb
   8       17  134216704 sdb1
btrfsボリュームを作成し、そのボリュームを/containerにマウントします。
$ mkfs.btrfs /dev/sdb1
$ echo "/dev/sdb1 /container btrfs defaults,noatime,_netdev 0 2" > /etc/fstab
$ mount -a
lxcのインストールで既にサーバの/containerディレクトリを作成しています。
続いて、以下のドキュメントを参照し、スクリプトを使って、2個目のvNICを構成します。Bare Metalインスタンスに対し、VMインスタンスでは少々違いがあります。
Overview of Networking - Virtual Network Interface Cards (VNICs)
https://docs.us-phoenix-1.oraclecloud.com/Content/Network/Tasks/managingVNICs.htm?Highlight=secondary_vnic
1個目のlxcコンテナを作成します。構文は以下の通りです。
lxc-create -n <container name> -t <template> -- -R <release>
  • container nameには利用したいコンテナ名を指定します。例えば、ol5とします。
  • Oracle Linuxコンテナを作成するために、templateとして"oracle"テンプレートを使います。
  • releaseでは、利用したいコンテナOSのリリースを指定します。今回はOracle Linux 5コンテナを作成しているので、-R 5.latest を使います。
  • Oracle Linux 4、6、7の場合、同じ "oracle" テンプレートを使いますが、<release> を4.latest、6.latest、7.latestに変更します。
$ lxc-create -n ol5 -t oracle -- -R 5.latest
Host is OracleServer 7.3
Create configuration file /container/ol5/config
Yum installing release 5.latest for x86_64
...
Added container user:oracle password:oracle
Added container user:root password:root
Container : /container/ol5/rootfs
Config    : /container/ol5/config
Network   : eth0 (veth) on lxcbr0
新規作成したコンテナのネットワーク構成を変更するという、追加の構成が必要です。
コンテナ構成ファイルを変更します。
$ vi /container/ol5/config
このファイルの以下の行
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.hwaddr = 00:16:3e:xx:xx:xx <- where xx:xx:xx has assigned values
を、次のように変更します。
lxc.network.type = phys
lxc.network.link = ens2f0.vlan.1  or ens4 or whatever name of the secondary vnic interface created earlier was called
lxc.network.hwaddr の行はコメントアウトもしくは削除します。
#lxc.network.hwaddr =
スクリプトが生成するインターフェースのMACアドレスを利用したいので、hwaddrの行をコメントアウトすることをお奨めします。
直接コンテナに効率的にネットワークインターフェースをパススルーするためにvethをphysに変更します。

コンテナを起動します。
$ lxc-start -n ol5.1
コンソールに接続します。
$ lxc-console -n ol5.1
デフォルトの root パスワードは root です。コンテナ作成後変更してください。
コンソールを終了するには、以下をタイプします。
ctrl-a q
以下のURLを確認すると、コンテナ内のネットワークを構成します。VNICのIP構成をインスタンス内から見つけることができます。
$ wget http://169.254.169.254/opc/v1/vnics/
以下は手作業で実施します。
$ ifconfig eth0 10.0.2.3 netmask 255.255.255.0
$ route add default gw 10.0.2.1
新たにifcfgスクリプトを作成し、起動時にネットワークを構成するようにします。
/etc/sysconfig/network-scripts/ifcfg-eth0 を編集します。編集例は以下の通りです。
DEVICE="eth0"
BOOTPROTO=none
ONBOOT=yes
TYPE="Ethernet"
IPADDR=10.0.2.3
PREFIX=24
GATEWAY=10.0.2.1
DEFROUTE=yes
どのlxcコンテナがアクティブかを確認するには、以下のコマンドを使います。
$ lxc-ls --active
このコンテナはOracle Linux 7上で動作する、サポートされるOracle Linux 5環境ができあがりました。

注:Oracle Linux 5はExtended Supportのフェーズに入っています。詳細は以下をご覧ください。
Oracle Linux 5 Extended Support Data Sheet
http://www.oracle.com/us/technologies/linux/oracle-linux-5-extended-support-3662088.pdf
Oracle Cloud Subscriptionのお客様は、SubscriptionにExtended Supportが含まれています。追加料金や手数料は不要です。

[Linux] Accelerating Linux Boot Time

原文はこちら。
https://blogs.oracle.com/linuxkernel/accelerating-linux-boot-time

このエントリはOracle LinuxカーネルチームのPasha Tatashinによるものです。

Linux Reboot

マシンが速やかに再起動することが重要な理由はたくさんあります。いくつか列挙してみましょう。
  1. 稼働時間と可用性の向上
  2. 顧客体験の向上:ブート/リブートプロセスが速いとマシン全体が速くなったように感じる
  3. ダウンタイムが短くなれば、重要なパッチを迅速に適用してセキュリティを向上させることが可能
上記のすべてが競争上の優位性を提供します。

Linux Early Boot Improvements

今年初めから、Linuxの起動と再起動のパフォーマンスを向上させる方法を検討してきました。これまでのところ、私は初期起動時のみを見ていて、いくつかの問題を特定しました。それらのうちのいくつかはすでにメインラインのLinuxにプッシュされており、一部は統合されている途中です。

ここでは、3064個のCPUと32TBのメモリを搭載した最大規模のSPARCマシンのLinuxの再起動時間を示します(ほとんどの修正は汎用コードに含まれていますが、大規模な構成の他のアーキテクチャでも同様の改善が見込まれます)。

Linux1 (before) Linux2  (after)
Shutdown 4m20s 4m20s
Firmware 1m13s 1m13s
Kernel 14m32s 0m58s
Services 8m44s 4m13s
Total 28m49s 10m44s

Note: 上記の結果に比べ、素のハードウェア上ではファームウェアの初期化は何倍も時間がかかります。このデータは仮想化I/Oを備えたLDomゲストで測定したものです。
  • Linux1は手を入れる前のメインラインカーネルです。
  • Linux2には以下の変更を追加しています。
    1. add_node_ranges() が遅い
      32TBのシステムでは、この関数は82秒を要しています。その理由は、このページが属するNUMAノードを決定するために1ページずつ辿るためです。この関数を最適化すると、1秒未満で完了するようになりました。この成果はすでにメインラインのLinuxに統合されています。
    2. より高速に起動するためにOpenFirmware CPUノードのキャッシュをスキップLinuxはOpenBootからすべてのCPUに関する情報をキャッシュします。そのため、3064個のCPUがあると75.56秒かかります。しかし、ハイパーバイザのマシン記述子にすべて含まれているため、sun4vのCPUノードに関する情報は実際には必要ありません。この修正を適用すると、ブート中にOpenBootからデータをキャッシュするのに1秒未満ですみます。この変更はまだメインラインに提出されていません。
    3. page構造体の並列ゼロ初期化
      起動中にmemblockアロケータが1スレッドだけを使いpage構造体に割り当てられたメモリをゼロ初期化します。この修正では、page構造体の遅延初期化機能を使用して、page構造体のゼロ初期化を並列処理します。この修正により、32Tマシンでは約73秒起動時間を短縮できます。パッチはアップストリームのコミュニティに提出され、現在レビュー中です。
    4. page構造体の遅延初期化の有効化(詳細は後述)
      Linuxには、ブート時にpage構造体の並列初期化オプションがあります。この修正により、起動時間が171秒短縮されます。現在のところ、SPARCではいくつかの依存作業(例えば、メモリのホットプラグ)が完了するまで、このオプションは無効になっています。
    5. 低速ハッシュテーブルの初期化
    6. inode_cachep、dentry_hashtableといった、alloc_large_system_hash()を使って割り当てられたハッシュテーブルは、メモリサイズが増加すると大きくなる可能性があります。これらの多くは単純にmemset()でゼロ埋めすることで初期化できます。また、一部は現在の増大速度ほど速く成長しません。今回、この問題に取り組むために新しい適応型スケーリングアルゴリズム(adaptive scaling algorithm)を追加しました。この修正により、起動時間が58秒短縮します。パッチはメインラインにあります。
    7. 初期ブートタイムスタンプ
      この作業ではパフォーマンスは向上しませんが、上記で見つかった多くの問題を検出できます。

Complete Deferred Page Initialization

システム内のすべての物理ページフレーム(physical page frame)には、関連するpage構造体があり、これを使ってページのステータスを記述します。起動時に、カーネルはすべてのpage構造体にメモリを割り当て、初期化します。従来、初期化はスレーブCPUがオンライン化される前に起動の早い段階で行われるため、大規模なマシンではかなりの時間がかかります。この問題は、Mel GormanがParallel struct page initialisation機能を追加した2015年に部分的に解決されました。
Parallel struct page initialisation v4
http://linux-kernel.2935.n7.nabble.com/PATCH-0-13-Parallel-struct-page-initialisation-v4-tt1094819.html
Melのプロジェクトでは、起動時にカーネルはpageの小さな部分集合のみを初期化し、残りを他のCPUが利用可能なときに初期化していました。ただし、page構造体の初期化時間をさらに短縮するには、次の2つの方法があります。
  1. 初期化を並列実行していたとしても、page構造体が格納されるメモリのゼロ埋めは(これまで同様)1個のスレッドだけを使って実施します。これはLinuxの初期ブートメモリアロケータ(memblock)が割り当て対象のメモリを常にゼロ埋めするためです。この問題を解決するためには、起動時のゼロ埋めを他のCPUが使用可能になった時点で実施するようにすることが必要です。しかし、カーネルのある部分がpage構造体のメモリを初期化またはゼロ埋めすることを期待しているため、複雑な課題に対応する必要があります。そのため、メモリがゼロ埋めされる前に、page構造体にアクセスさせないようにする必要があります。このプロジェクトは現在レビュー中であり、1TBのメモリを搭載するOracle X5-8では起動が22秒早くなります。
    complete deferred page initialization
    https://lwn.net/Articles/734374/
    時間短縮は搭載メモリサイズに比例し、X5-8は最大6TBまで搭載可能なので、フル構成のX5-8ではブート時間は約132秒短縮できます。
    Oracle Server X5-8—Features
    https://www.oracle.com/servers/x86/x5-8/features.html
    このプロジェクトでは、既存のブート時のバグを修正し、今後新しいバグが入り込まないようにするため、memblockアロケータにデバッグチェックを追加しています。
  2. もう一つの、page構造体の初期化を改善するための最適化は、ノードごとに複数のスレッドを使用して構造体ページを初期化することです。1つのメモリノードには、数ギガバイトのメモリを含めることができます。一例として、6TBのメモリを備えたX5-8はノードあたり0.75TBのメモリを持ち、この値は新しいシステムで増加し続けます。新しいXeonプロセッサはノードあたり最大1.5TBをサポートし、AMDのEPYCプロセッサはノードあたり最大2TBをサポートします。
    Intel® Xeon® Platinum 8180M Processor 38.5M Cache, 2.50 GHz
    Product Specifications
    https://ark.intel.com/products/120498/Intel-Xeon-Platinum-8180M-Processor-38_5M-Cache-2_50-GHz
    EPYC™ 7000 Series
    http://www.amd.com/en/products/epyc-7000-series
    EPYC™ 7000 Series Data Sheet
    http://www.amd.com/system/files/2017-06/AMD-EPYC-Data-Sheet.pdf
    Daniel Jordanは、現在レビュー中の彼のktaskの作業にこの最適化を含めました。
    ktask: multithread cpu-intensive kernel work
    https://lwn.net/Articles/731925/
    ktaskについては以下のエントリをご覧ください。
    ktask: A Generic Framework for Parallelizing CPU-Intensive Work
    https://blogs.oracle.com/linuxkernel/ktask%3A-a-generic-framework-for-parallelizing-cpu-intensive-work
    https://orablogs-jp.blogspot.jp/2017/11/ktask-generic-framework-for.html

Early Boot Time Stamps

Linuxには、コンソールに表示される各行の先頭にタイムスタンプを入れるのに便利な機能があります。CONFIG_PRINTK_TIMEはデフォルトで多くのディストリビューションで有効になっています。
CONFIG_PRINTK_TIME: Show timing information on printks
https://cateee.net/lkddb/web-lkddb/PRINTK_TIME.html
これを使えば、dmesg -dコマンド(dmesgの新しいバージョンでのみ-dフラグを利用可)を使用して、起動時および実行時のregressionをすばやく検出できます。
dmesg - print or control the kernel ring buffer
http://man7.org/linux/man-pages/man1/dmesg.1.html
しかし、タイムスタンプはブート時の最終段階で利用できるため、ブートにかなりの時間を費やすと、その時間が報告されません。実際、このエントリで説明しているすべての改善は、printkタイムスタンプが利用可能になる前に発生します。したがって、起動時にタイムスタンプを利用できるようにするために、SPARC用とx86用の2つのプロジェクトで作業が行われました。 SPARCはすでにメインライン上にありますが、x86は現在レビュー中です。
[v3 0/8] Early boot timestamp
https://www.spinics.net/lists/sparclinux/msg18063.html
[PATCH v6 0/4] Early boot time stamps for x86
https://lkml.org/lkml/2017/8/30/574

[Cloud] Announcing Commvault Integration with Oracle Cloud Infrastructure

原文はこちら。
https://blogs.oracle.com/cloud-infrastructure/announcing-commvault-integration-with-oracle-cloud-infrastructure-v2

Commvaultの統合とOracle Cloud Infrastructure(OCI)のサポートを発表できわくわくしています。お客様は、Commvaultソフトウェアを使用してバックアップ対象としてOracle Cloud Infrastructure Object Storageを使用できるようになりました。
Oracle Cloud Infrastructure > Storage
https://cloud.oracle.com/ja_JP/infrastructure/storage
Commvaultは、オンプレミスおよびクラウドベースのワークロードにエンタープライズクラスのデータ保護テクノロジを提供します。このコラボレーションにより、管理者がバックアップ、リカバリおよびアーカイブを一貫して管理しながら、Oracle Database、Oracle以外のデータベース、仮想マシン、ファイル、ログなどのすべてのエンタープライズ・ワークロードのバックアップおよびリカバリが可能です。
Commvaultのサポートは、OCI Object Storageとのネイティブな統合に基づいています。Commvaultは、データ重複除外とデータ暗号化においてエンタープライズクラスの機能を提供します。OCI Object Storageに送信する前に、Commvaultを使用してデータを重複排除および暗号化することができます。 Commvaultには完全かつ独立したコピー保持管理機能もあります。Oracle Cloudでクラウド固有の作業負荷を保護する場合、この統合によりオンプレミスおよびクラウド間のツールセットが統合整理され、コストの削減、複雑さの低減、および再トレーニングの労力を最小限に抑えます。

以下はCommvaultでバックアップターゲットとして、OCI Object Storageを構成するために必要なものをまとめたものです。

OCI Object Storage Configuration on Commvault Console

Configure Cloud Storage Library

1. Cloud Storage Libraryの追加


2. OCI Object Storageの構成




Backup and Restore

新たに構成したOCI Object Storageを使ってバックアップやリストアを実行するためには、Storage PolicyとBackup Sub-clientが必要です。

1. ポリシーを右クリックしてStorage Policyをセットアップします。New Storage Policyを選択します。


2. Sub-clientを作成します。


3. Backupを実行します。


4. Job Controllerからバックアップのジョブを監視します。


5. Restoreを実行します。




この統合の目的は、CommvaultをOCI Object Storageのために構成する際にシームレスな体験を提供することにあります。OCI Object Storageの資格証明のセット、つまりObject Storageのエンドポイント、Access KeyとSecret Keyさえあればよいのです。

まずは、オンラインドキュメントやCommvaultの最新のブログエントリをご覧になるところからはじめてください。
Oracle Cloud Infrastructure - Services
Overview of Object Storage
https://docs.us-phoenix-1.oraclecloud.com/Content/Object/Concepts/overview.htm
COMMVAULT SOFTWARE SIMPLIFIES USING ORACLE CLOUD
https://www.commvault.com/blogs/2017/october/commvault-software-simplifies-using-oracle-cloud
データ保護、バックアップ、復元をより簡単に実施できるようにするために、皆様のご意見をお寄せください。

[Linux] ktask: A Generic Framework for Parallelizing CPU-Intensive Work

原文はこちら。
https://blogs.oracle.com/linuxkernel/ktask%3a-a-generic-framework-for-parallelizing-cpu-intensive-work

コアカーネルチームのDaniel Jordanがktaskフレームワークのレビューを書きました。ktaskフレームワークは、Linux Kernelメーリングリストに提出され、現在レビュー中です。

直近ではLinuxカーネルに大きなタスクを並列化させるktaskというプロジェクトに取り組んできました。これらのタスクは常にカーネル・コンテキストで実行されますが、カーネル内で直接開始することも、アプリケーションからシステムコールを通じて間接的に開始することもできます。
このエントリでは、このフレームワークが必要になった課題、フレームワークの背後にあるハイレベルの思想、これまでに実装したユースケース、およびプロジェクトに関するその他の詳細について説明します。最後に、各タスクが効率的に並列化されていることを確認するために使用したLinuxパフォーマンスツールを説明します。

Motivation

コア数やメモリサイズが増えてもアプリケーションとカーネル自体が引き続きうまく稼働するようにするためには、カーネルのスケーリングを行う必要があります。例えば、システムコールがシステムリソースの特定の部分を呼び出す場合、カーネルは同様の割合のシステムリソースを使用して要求に対応するようにして対応する必要があります。 しかし、いくつかの場所では、カーネルがまだ動作していない場合があります。
たとえば、データベースやインメモリ・キャッシュなどの大きなアプリケーションがシャットダウンしてページをシステムに返した場合、そのアプリケーションはマシンのメモリの半分を簡単に使用していた可能性があります。しかし現在のところ、解放されるメモリのサイズにかかわらず、カーネルはこの作業にたった1スレッドを割り当てています。この部分がボトルネックになって、大規模なアプリケーションがすべてのリソースを返すために数分かかってしまい、その間に他のアプリケーションがそれらのリソースを使用することができなくなる可能性があります。

Concept

ktaskとは、これらのタイプの重い要求を処理するために使用されるカーネルスレッドの数をスケーリングするように設計されたものです。
コンセプトはかなりシンプルですが、用語を説明しておきます。タスク(task)とは、実行すべき総作業であり、チャンク(chunk)はスレッドに与えられた作業の単位です。
ktaskフレームワークを使ってタスクを完了するために、(システムコールなどの)カーネル・クライアントが、1つのチャンクを完了させるスレッド関数を提供します。スレッド関数は、クライアントがタスクに固有のデータを渡すために使用する引数だけでなく、チャンクを区切るstart引数とend引数を標準的な方法で定義されます。
さらに、クライアントは、タスクの開始を表すオブジェクトと、タスクのいくつかのユニットを進めて新しいタスク位置を表す別のオブジェクトを生成する方法を知っているイテレータ関数を提供します。ktaskフレームワークは、開始オブジェクトとイテレータを内部的に使用して、タスクをチャンクに分割します。
最後に、クライアントは、1チャンクで処理するのに適した最小作業量を示すために、タスクの合計サイズとチャンクの最小サイズを渡します。サイズはタスク固有の単位(ページ、inode、バイトなど)で与えられます。ktaskフレームワークは、オンラインのCPU個数と内部の最大スレッド数とともに、これらのサイズを使用して、何個のスレッドを開始すべきか、タスクを何個のチャンクに分割すべきかを決定します。
たとえば、巨大なページをクリアするタスクを考えてみると、このタスクは構成要素の各ベースページのページクリア関数を呼び出す 'for'ループを伴う単一のスレッドで行われていました。ktaskを使って並列化するために、クライアントはまずforループをスレッド関数に移動し、関数に渡された範囲で動作するようにします。この単純なケースでは、スレッド関数の開始引数と終了引数は、huge pageの一部をクリアするためのアドレスに過ぎません。次に、 forループが使用されていた箇所で、クライアントは、huge pageの開始アドレス、huge pageの合計サイズ、およびスレッド関数を使用してktaskを呼び出します。内部的には、ktaskはアドレス範囲を適切な数のチャンクに分割し、適切な数のスレッドを開始してそれらを完了します。

インターフェイスの詳細については、最新のアップストリームパッチセットを参照してください。
[RFC PATCH v2 2/7] ktask: multithread cpu-intensive kernel work
https://lkml.org/lkml/2017/8/24/801

Use Cases and Performance Results

これまでのところ、ktaskは、システムが匿名ページを解放するパスのunmap (2) やexit (2) 、ブート時にページの初期化を構成し、最大のhuge pageサイズをゼロにするといった箇所で使用することが予定されています。huge pageをクリアする簡単な例で、ktaskフレームワークのパフォーマンスを説明します。

以下の結果は下表のスペックを持つOracle X5-8 serverで計測しました。
CPU type: Intel(R) Xeon(R) CPU E7-8895 v3 @ 2.60GHz
CPU count: 144 cores (288 threads); 8 nodes @ 18 cores/node
Memory: 1T
以下はテスト結果です。1つのNUMAノード内と、大規模なマルチノードシステム上のすべてのノードにわたるスケーラビリティを示すために、4つのレンジサイズを使用しました。最初のサイズ(100GB)は、完全に同じノードのページから構成されています。サイズが最初のサイズを超えて増加するにつれて、ゼロ初期化されたメモリは増大し、次第にシステムのノードの多くを含んでいきます。
nthread   speedup   size (GiB)   min time (s)   stdev
     1                    100          41.13    0.03
     2     2.03x          100          20.26    0.14
     4     4.28x          100           9.62    0.09
     8     8.39x          100           4.90    0.05
    16    10.44x          100           3.94    0.03

     1                    200          89.68    0.35
     2     2.21x          200          40.64    0.18
     4     4.64x          200          19.33    0.32
     8     8.99x          200           9.98    0.04
    16    11.27x          200           7.96    0.04

     1                    400         188.20    1.57
     2     2.30x          400          81.84    0.09
     4     4.63x          400          40.62    0.26
     8     8.92x          400          21.09    0.50
    16    11.78x          400          15.97    0.25

     1                    800         434.91    1.81
     2     2.54x          800         170.97    1.46
     4     4.98x          800          87.38    1.91
     8    10.15x          800          42.86    2.59
    16    12.99x          800          33.48    0.83
このテストでは最大8スレッドまでスケールしていますが、16スレッドでは壁に当たっています。これはチップのメモリ帯域幅の上限に達したためです。実際には、より多くのスレッドがより多くのチップのキャッシュを使用できるため、2〜8スレッドまでは非常にリニアに高速化していることがわかります。
ここで強調しているループはclear_page_erms という、タイトループ内でわずかな命令を使うプラットフォーム固有のページクリア関数で、1つのスレッドで最高2550 MiB/sの帯域幅に達します。 2スレッド、4スレッド、または8スレッド利用時に各スレッドで同じ帯域幅を得られますが、16スレッドではスレッドごとの帯域幅が1420 MiB/sに低下します。
しかし、ktaskがNUMAを認識することでのパフォーマンスも向上します(ktaskは、実行されている作業のローカルノード上でワーカースレッドを開始します)。このことは性能向上のより大きな要素になります。それは、ゼロ初期化するページの量が複数のノードからのメモリを含んで増大するにつれ、メモリサイズが増加するにつれて実際にスピードアップするという、優れたスケーラビリティの利点を得られからです。

Tools Used to Build this Framework

このフレームワークは、huge pageの消去やmunmap(2)のような、パス最適化時の2つのステップのうちの最初のものに過ぎません。ホットロック、キャッシュラインバウンシング、スレッド間の冗長な作業といった効率的な並列化の障害を取り除くために、カーネルコードをさらにチューニングする必要があります。幸いにもLinuxカーネルには、これらの問題を診断する上で役立つ多くのツールが付属しています。
最も使用したツールはlock_statです。
lock statistics (lock_stat)
https://www.kernel.org/doc/Documentation/locking/lockstat.txt
これは競合カウント、ロックの獲得数、待ち時間といった、さまざまなカーネルのロックに関するメトリックを収集する、ユーザ空間にアクセスできるツールです。lock_statを使用するには、CONFIG_LOCK_STAT=y を指定してカーネルをビルドする必要があります。カーネルからエクスポートされた多くのユーティリティと同様に、lock_statはprocファイルによって制御されます。まず /proc/lock_stat は収集されたデータを表示し、続いて /proc/sys/kernel/lock_stat はツールを有効化または無効化します。このエントリに添付されている単純なラッパースクリプトは、こうした作業をすべて自動化するものです。そのため、スクリプトを実行するだけでよいのです。
lstat cmd...
コマンドが戻ると、 lock_stat が無効化され、/proc/lock_stat をのんびりご覧頂くことができます。将来のシステム動作によってデータが汚れることを心配する必要はありません。-fオプションを使用すると、データをファイルに書き出すことができます。

もう1つの便利なツールは、perf probeで、これはコードを変更せずに動的なトレースポイントを実行中のカーネルに追加できるperfサブコマンドです。
perf-probe(1)
https://raw.githubusercontent.com/torvalds/linux/master/tools/perf/Documentation/perf-probe.txt
今回の場合、workqueueのスレッド待ち時間を調べるために使用しましたが、トレースする特定の関数をトレースしたい場合、一般にperf probeは有用です。どのCPUで関数が実行されたのか、関数(entry、return、さらには特定の命令)内のどの時点でいつ実行されたのかを表示することができます。利用にあたっては、カーネルを以下の設定で構成する必要があります。
CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBES=y
CONFIG_PERF_EVENTS=y
以下は、あらかじめ定義されたイベントと動的プローブを組み合わせて、前述のスレッド待ち時間を測定するperf probeのサンプルです。この実験の目的は、ある状況下で、ktaskクライアントがktask_runを呼び出し、ktaskスレッド関数(ここではdispose_list_task)を実行するまでの間に、workqueueスレッドが原因で大きな遅延になっていないことを検証することでした。dispose_list_task関数はevict2と呼ばれる別の関数を多く呼び出すため、並列化に適しています。perf probeは、このプロセスの各ステップがいつどこで起こったかを示すのに役立ちます。

まず、動的なプローブを冗長に (-v) 追加 (-a) します。
# perf probe -v -a evict_inodes
# perf probe -v -a ktask_run
# perf probe -v -a ktask_task
# perf probe -v -a 'dispose_list_task start:x64 end:x64'             # fourth probe
# perf probe -v -a 'evict2 inode:x64'
# perf probe -v -a 'evict2_ret=evict2+309 inode:x64'                 # sixth probe
# perf probe -v -a 'dispose_list_task_ret=dispose_list_task%return'
# perf probe -v -a 'ktask_task_ret=ktask_task%return'
# perf probe -v -a 'ktask_run_ret=ktask_run%return'
# perf probe -v -a 'evict_inodes_ret=evict_inodes%return'
最初の3つは、カーネルの関数名を使用して、プローブを関数呼び出し時に呼び出すようにします。4番目のプローブdispose_list_taskは、関数呼び出し時にも起動しますが、関数の引数(startおよびend)を64ビットの16進値として出力します。6番目はevict2の特定の命令で呼び出されます。残りのプローブはそれぞれの関数から戻ったタイミングで呼び出されます。

今回は設定したダイナミックプローブを使用して、実際に関心のあるコマンドを記録します。'probe:'という接頭辞が付いていないプローブ(ここでは例えば 'workqueue:' )は、最近のすべてのカーネルに表示されるあらかじめ定義されたperfイベントです。
# perf record -aR                           \   
    -e probe:evict_inodes                   \   
    -e probe:ktask_run                      \   
    -e workqueue:workqueue_queue_work       \   
    -e workqueue:workqueue_activate_work    \   
    -e workqueue:workqueue_execute_start    \   
    -e probe:ktask_task                     \   
    -e probe:dispose_list_task              \   
    -e probe:evict2                         \   
    -e probe:evict2_ret                     \   
    -e probe:dispose_list_task_ret          \   
    -e probe:ktask_task_ret                 \   
    -e workqueue:workqueue_execute_end      \   
    -e probe:ktask_run_ret                  \   
    -e probe:evict_inodes_ret               \     
    cmd...
-aフラグを指定すると、システム内のすべてのCPU上のイベントが記録されます。 通常、perfは与えられたコマンドのイベントだけを記録しますが、この場合はカーネル内のworkqueueスレッドもトレースします。
# chown user:group perf.record.out
root権限で不必要に実行されないようにファイルのパーミッションを変更した後、以下のようにperfレコードから生成されたraw出力を後から処理できます。
$ perf script -F cpu,event,time,trace > perf.script.out
$ cat perf.script.out
[006]        0.000000:                probe:evict_inodes: (ffffffff811f6c20)
[006]        0.014580:                   probe:ktask_run: (ffffffff8107e210)
[006]        0.014584:    workqueue:workqueue_queue_work: work struct=0xffff8818634b6058 function=ktask_task workqueue=0xffff883fef931a00 req_cpu=1 cpu=1
[006]        0.014585: workqueue:workqueue_activate_work: work struct 0xffff8818634b6058
...snip...
[001]        0.014645: workqueue:workqueue_execute_start: work struct 0xffff8818634b6058: function ktask_task
[001]        0.014667:                  probe:ktask_task: (ffffffff8107e0c0)
[001]        0.014671:           probe:dispose_list_task: (ffffffff811f5a50) start_x64=0x0 end_x64=0x1
[001]        0.014673:                      probe:evict2: (ffffffff811f5890) inode_x64=0xffff8818a15fbb50
[001]        0.016089:                  probe:evict2_ret: (ffffffff811f59c5) inode_x64=0xffff8818a15fbb50
[001]        0.016090:                      probe:evict2: (ffffffff811f5890) inode_x64=0xffff881fc9fceb10
[001]        0.017483:                  probe:evict2_ret: (ffffffff811f59c5) inode_x64=0xffff881fc9fceb10
...snip...
[001]        0.193898:                      probe:evict2: (ffffffff811f5890) inode_x64=0xffff8816939c6b10
[001]        0.195335:                  probe:evict2_ret: (ffffffff811f59c5) inode_x64=0xffff8816939c6b10
[001]        0.195339:       probe:dispose_list_task_ret: (ffffffff811f5a50 <- ffffffff8107e134)
[001]        0.195345:           probe:dispose_list_task: (ffffffff811f5a50) start_x64=0x15 end_x64=0x16
[001]        0.195347:                      probe:evict2: (ffffffff811f5890) inode_x64=0xffff8819a55f2350
[001]        0.196753:                  probe:evict2_ret: (ffffffff811f59c5) inode_x64=0xffff8819a55f2350
...snip...
[001]        2.701235:                      probe:evict2: (ffffffff811f5890) inode_x64=0xffff8816fdb52290
[001]        2.702268:                  probe:evict2_ret: (ffffffff811f59c5) inode_x64=0xffff8816fdb52290
[001]        2.702269:       probe:dispose_list_task_ret: (ffffffff811f5a50 <- ffffffff8107e134)
[001]        2.702273:              probe:ktask_task_ret: (ffffffff8107e0c0 <- ffffffff81072c69)
[001]        2.702275:   workqueue:workqueue_execute_end: work struct 0xffff8818634b6058
...snip...
[006]        2.706126:               probe:ktask_run_ret: (ffffffff8107e210 <- ffffffff811f6e0a)
[006]        2.706129:            probe:evict_inodes_ret: (ffffffff811f6c20 <- ffffffff811db4b4)
左から、括弧内のCPU番号、秒単位のイベントのタイムスタンプ、プローブ名が並んでいます。リクエストした追加のデータは、行末に表示されます。この例では、ktask_runの呼び出しから1スレッドの最初のチャンクの開始時間(probe : dispose_list_task)が100マイクロ秒(0.014671 - 0.014580)未満であったことがわかりました。これから、workqueueスレッドの待ち時間が問題ではないことがわかります。

最後に、システムから動的トレースポイントを削除します。
# perf probe -d '*'

Conclusion

このエントリで、ktaskのモチベーションとなったスケーラビリティに関する問題、フレームワークのコンセプト、ユースケースを説明し、最後にフレームワーク作成時に使用したパフォーマンスツールについてまとめました。
これからは、ktaskに対するより多くのフィードバックをいただき、より多くのCallerを追加し、コアフレームワークを強化し続けることを計画しています。ktaskが近い将来、アップストリームに進出し、すべての人がカーネルスケーラビリティの向上を享受できるようになることを願っています。

[Cloud, Java] Oracle Cloud Application Development Platform 17.4.2 Anncounced

原文はこちら。
https://blogs.oracle.com/imc/oracle-cloud-application-development-platform-1742-anncounced

Oracle Application Container CloudおよびOracle Java Cloud Serviceが機能強化され、17.4.2として展開を始めたことを発表できうれしく思っています。

New Language runtime Support – Go

ACCSでは、既存のランタイム(Java SE、Node、PHP、Python、Ruby、Java EE)に加え、Go言語(golang)をサポートしました。
ぜひクイックスタートチュートリアルを試してみてください!(だじゃれですな…)

Worker Applications

パブリック・インターネットに公開せずにバックエンド・アプリケーション(Worker)をデプロイできるようになりました。つまり、ACCSは外部インターネットからアクセス可能なURLをアプリケーション向けに生成しないだけで、通常のACCSアプリケーションのその他の機能はすべて備えています。manifest.jsonの明示的な宣言さえすればOKです(下記の例を参照)。
{
  "runtime": {
  "majorVersion": "8"
},
"command": "java -jar accs-worker.jar",
"type": "worker",
"notes": "ACCS worker app"
}
このエントリのResourcesをチェックしてください。

Secure applications

ACCSを使って、シームレスにOracle Identity Cloud Serviceと統合し、アプリケーションの認証・認可を実現できるようになりました。この機能は現在Node.jsおよびJava SEアプリケーションランタイムにデプロイされているアプリケーションでご利用頂けます。

Runtime Updates - Java EE 7 and Java 9

これまで、Java EEアプリケーションを実行するためには自身で、embedded/packaged TomcatやJBoss、Payara Microなどコンテナを持ち込む必要がありましたが、17.3.5から、Java EE 7を第1級のランタイムとしてサポートするようになりました。今では単純にWARファイルをACCSにデプロイすればよくなりました。

Java SEランタイムのサポートがアップグレードされて、先頃リリースされたJava 9が追加されました。新たにJava 9アプリケーションをデプロイするか、古いバージョン(Java 7,8)からアップグレードすることができます。

Java Cloud Service

Oracle Identity Cloud Serviceのサブスクリプションが含まれている場合、Oracle Java Cloud Serviceは、新しいサービス・インスタンスのプロビジョニング中にWebLogic Serverセキュリティ・レルムをOracle Identity Cloudサービスと統合できます。統合後は、管理コンソールや関連するアプリケーションは、Oracle Identity Cloudサービスに対して認証されます。
サービス・インスタンスでOracle Identity Cloud Serviceを有効にすると、Oracleが管理するLoad Balancerが自動的に構成されます。このロードバランサは、サービスインスタンスの外部で実行されます。サービス・インスタンスでOracle Identity Cloud Serviceを有効にしない場合は、以前のリリースと同様にローカル・ロード・バランサを構成できます。

Resources

[WLS] Migrating from Multi Data Source to Active GridLink - Take 2

原文はこちら。
https://blogs.oracle.com/weblogicserver/migrating-from-multi-data-source-to-active-gridlink-take-2

このトピックに関するオリジナルのブログエントリで、マルチデータソース(MDS)を削除し、Active GridLinkデータソース(AGL)に置き換えることを提案しました。
Migrating from Multi Data Source to Active GridLink
https://blogs.oracle.com/weblogicserver/migrating-from-multi-data-source-to-active-gridlink
https://orablogs-jp.blogspot.jp/2013/04/migrating-from-multi-data-source-to.html
実際には、マルチデータソースはJDCストアのように別のオブジェクトから参照される可能性が高く、マルチデータソースを削除すると無効な構成が作成されてしまいます。さらに、マルチデータソースからの接続を使うオブジェクトが再構成中、ならびに再構成後に失敗するでしょう。これはつまり、こうした類の操作では、関連するサーバーをシャットダウンし、構成をオフラインWLSTで更新し、サーバーを再起動する必要があり、管理コンソールを使うことはできません。ただし、コンソールを利用する説明をしている部分を除いては、以前のエントリの別の情報はこのプロセスでも対応しています。JNDI名は変更していないので、構成だけを変更します。アプリケーションに対して変更する必要はありません。
以下は、オフラインWLSTスクリプトのサンプルで、これをパラメータ化し、複数のデータソースを扱えるように柔軟性をあげることもできるでしょう。
# java weblogic.WLST file.py
import sys, socket, os
# Application values
dsName='myds'
memberds1='ds1'
memberds2='ds2'
domain='/domaindir
onsNodeList=host1:6200,host2:6200'
url='jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=' \
 + '(ADDRESS=(PROTOCOL=TCP)(HOST=host1)(PORT=1521))' \
 + '(ADDRESS=(PROTOCOL=TCP)(HOST=host2)(PORT=1521)))' \
 + '(CONNECT_DATA=(SERVICE_NAME=servicename)))'
user='user1'
password='password1'
readDomain(domain)
# change type from MDS to AGL
# The following is for WLS 12.1.2 and 12.1.3 if not setting
# FanEnabled true, which is not recommended
# set('ActiveGridlink','true')
# The following is for WLS 12.2.1 and later
# cd('/JDBCSystemResources/' + dsName + '/JdbcResource/' + dsName )
# set('DatasourceType', 'AGL')
# set the AGL parameters
cd('/JDBCSystemResources/' + dsName + '/JdbcResource/' + dsName)
create('myJdbcOracleParams','JDBCOracleParams')
cd('JDBCOracleParams/NO_NAME_0')
set('FanEnabled','true')
set('OnsNodeList',onsNodeList)
# Set the data source parameters
cd('/JDBCSystemResources/' + dsName + '/JdbcResource/' + dsName + '/JDBCDataSourceParams/NO_NAME_0')
set('GlobalTransactionsProtocol','None')
unSet('DataSourceList')
unSet('AlgorithmType')
# Set the driver parameters
cd('/JDBCSystemResources/' + dsName + '/JdbcResource/' + dsName)
create('myJdbcDriverParams','JDBCDriverParams')
cd('JDBCDriverParams/NO_NAME_0')
set('Url',url)
set('DriverName','oracle.jdbc.OracleDriver')
set('PasswordEncrypted',password)
create('myProps','Properties')
cd('Properties/NO_NAME_0')
create('user', 'Property')
cd('Property')
cd('user')
set('Value', user)
# Set the connection pool parameters
cd('/JDBCSystemResources/' + dsName + '/JdbcResource/' + dsName)
create('myJdbcConnectionPoolParams','JDBCConnectionPoolParams')
cd('/JDBCSystemResources/' + dsName + '/JdbcResource/' + dsName + '/JDBCConnectionPoolParams/NO_NAME_0')
set('TestTableName','SQL ISVALID')
# remove member data sources if they are not needed
cd('/')
delete(memberds1, 'JDBCSystemResource')
delete(memberds2, 'JDBCSystemResource')
updateDomain()
closeDomain()
exit()
WLS 12.1.2および12.1.3では、FanEnabledがtrue(推奨されていません)に設定されておらず、auto-ONS(12.1.2 がauto-ONSをサポートする最初のリリースです)が使用されている場合でも、Active GridLinkデータソースを識別するために使用可能な明示的なActiveGridlinkフラグがあります上記のスクリプトで、FanEnabledとOnsNodeListが設定されていない場合にのみ、コメントを解除して設定してください。
WLS 12.2.1以降、JDBCResourceレベルで明示的なデータソースタイプがあります。 これがGENERICに設定されている場合は、set("DatasourceType", "AGL") を使って再設定する必要があります。上記のスクリプトでは、行のコメントを解除して設定します。この場合、ActiveGridlinkフラグは必要ありません。
ONSでOracle walletを使っている場合、JDBCOracleParamsオブジェクトにも追加する必要があります。WLS 12.2.1より前のリリースでは、ONSの情報は明示的に指定する必要がありますが、WLS 12.2.1以後ではONSの情報を指定する必要はなく、データベースが正しい情報を判断しようとします。より複雑なONSトポロジーについては、以下のドキュメントで説明しているフォーマットを使って構成を指定することができます。
Oracle® Fusion Middleware Oracle WebLogic Server JDBCデータ・ソースの管理 12c (12.2.1.2.0)
ONSホストおよびポートの構成
https://docs.oracle.com/cd/E84527_01/wls/JDBCA/gridlink_datasources.htm#GUID-A4965575-6753-4443-A2D5-58EFC7812B1C
Oracle® Fusion Middleware Administering JDBC Data Sources for Oracle WebLogic Server 12c (12.2.1)
Configure ONS Host and Port
http://docs.oracle.com/middleware/1221/wls/JDBCA/gridlink_datasources.htm#BABGJBIC