入门指南#

为了利用 RDMA 和 ATS 实现高性能计算,本指南概述了以下步骤

  • 配置 NVIDIA ConnectX-6 Dx 以支持 RoCE

  • 在 VMware ESXi 和虚拟机上启用 ATS

  • 在 NVIDIA ConnectX-6 DX 网卡上启用 ATS

  • 配置 NUMA 亲缘性

  • 创建用于多节点训练的 Docker 文件

  • 在多节点集群上的虚拟机之间设置无密钥登录

  • 运行 ResNet-50 多节点训练示例

配置 NVIDIA ConnectX-6 Dx 网卡和 Spectrum 交换机以支持 RoCE#

为了利用 RoCE,NVIDIA ConnectX-6 Dx 网卡必须在基于 DSCP 的 QoS 模式下的丢包网络上运行 RoCE。以下知识库文章是应用此配置的有用资源:https://community.mellanox.com/s/article/lossless-roce-configuration-for-mlnx-os-switches-in-dscp-based-qos-mode

在本指南中,我们将参考知识库文章中针对 3.8.2008 及更高版本的配置步骤。

  1. 在 NVIDIA 交换机上运行以下命令

    switch (config) # roce
    

    注意

    RoCE 功能已实现自动化,因此在无损网络上运行 RoCE 所需的全部操作就是运行 roce 命令。

  2. 创建一个隔离的 vLAN,并将 NVIDIA ConnectX 网卡作为接入端口放入创建的 vLAN 中。连接到交换机端口 1/1 - 1/4 的四台服务器。

    1switch (config) # interface vlan 111
    2switch (config vlan 111) # exit
    3switch (config) # interface ethernet 1/1-1/4 switchport access vlan 111
    
  3. 将接口上的 MTU 设置为 9216(在 3.9.2110 以下版本中,交换机的默认 MTU 为 1500)。

    1switch (config) # interface ethernet 1/1-1/4 shutdown
    2switch (config) # interface ethernet 1/1-1/4 mtu 9216
    3switch (config) # interface ethernet 1/1-1/4 no shutdown
    
  4. 可选,如果您运行的是 Cumulus Linux,请按照以下说明启用 RoCE:https://docs.cumulusnetworks.com/cumulus-linux-42/Network-Solutions/RDMA-over-Converged-Ethernet-RoCE/

在 VMware ESXi 和虚拟机上启用 ATS#

为了通过高性能实现对等 (P2P),我们将通过更新 VMKernel,然后更新 VM 配置来启用 ATS。

  1. 更新 VMKernel 以支持对等 (P2P)。

    • 要启用 ATS 启动选项,请调用以下命令并重新启动 ESXi

      esxcli system settings kernel set -s atsSupport -v TRUE
      
    • 重新启动后验证值是否正确,调用

      esxcli system settings kernel list -o atsSupport
      
    • 输出应类似于以下内容

      1Name          Type     Configured  Runtime   Default  Description
      2------------  -------  ----------  -------   -------  -----------
      3atsSupport    Bool     TRUE        TRUE      FALSE    Enable Support for PCIe ATS
      
  2. 更新 VM 配置以支持 P2P。

  3. 编辑 VM 配置设置

    1pciPassthru.allowP2P=true                       # enable P2P
    2pciPassthru.RelaxACSforP2P=true         # update ACS capabilities in switch
    

    注意

    当启用为 P2P 放宽 ACS 时,VMware 将找到支持 ATS 的直通设备,找到其父交换机,并启用 ACS 直接转换位。之前关于对等网络设备的所有功能必须提供给单个 VM 的限制已被取消。对等设备的每个功能都可以提供给单独的 VM。

  4. 如果存在多个 GPU 物理设备,VM 可以使用现有配置为 P2P 指定特定设备

    pciPassthru0.cfg.gpu-pci-id = "ssss:bb:dd.f"
    

    注意

    gpu-pci-id 是十六进制 SBDF 格式。如果 GPU 处于 SR-IOV 模式,则应指定 VF 地址。

在 NVIDIA ConnectX-6 Dx 网卡上启用 ATS#

  1. 使用以下命令安装 python 2.7

    sudo apt-get install python
    
  2. 下载并安装 MLNX OFED 5.0:https://network.nvidia.com/products/infiniband-drivers/linux/mlnx_ofed/

    • 选择(操作系统/版本/架构)并下载 tar 文件,例如:(Ubuntu/20.04/x86_64)。

    • 下载后,将软件包复制到 VM,并运行以下命令解压并安装

      1tar xvf MLNX_OFED_LINUX-5.2-2.2.4.0-ubuntu20.04-x86_64.tgz
      2cd  MLNX_OFED_LINUX-5.2-2.2.4.0-ubuntu20.04-x86_64.tgz
      3sudo ./mlnxofedinstall
      

      注意

      上述步骤还将更新所有 CX5 或 CX6 卡的固件。

    • 安装完成后运行以下命令

      sudo /etc/init.d/openibd restart
      

      注意

      在安装过程中,将检测到 CX-6 网卡,并且 OFED 应更新固件。如果失败,请下载最新的固件并手动更新。之后重复 OFED 安装。

  3. 使用以下命令检查 OFED 和固件版本

    1dpkg -l | grep mlnx-ofed
    2cat /sys/class/infiniband/mlx5*/fw_ver
    
  4. 启动 Mellanox 软件工具

    sudo mst start
    
  5. 使用以下命令检查 CX-6 网卡的 ATS_ENABLED 配置状态。您应该看到类似于以下的输出

    1sudo mlxconfig -d /dev/mst/mt4123_pciconf0 query | grep -i ATS
    2ATS_ENABLED                         False(0)
    
  6. 如果不存在,则固件不支持 ATS。请更新到支持 ATS 的固件版本。如果设置为 False,请使用以下命令启用 ATS

     1sudo mlxconfig -d /dev/mst/mt4123_pciconf0 set ATS_ENABLED=true
     2Device #1:
     3----------
     4Device type:    ConnectX6
     5Name:           MCX653105A-HDA_Ax
     6Description:    ConnectX-6 VPI adapter card; HDR IB (200Gb/s) and 200GbE; single-port QSFP56; PCIe4.0 x16; tall bracket; ROHS R6
     7Device:        /dev/mst/mt4123_pciconf0
     8
     9Configurations:           Next Boot     New
    10ATS_ENABLED               False(0)      True(1)
    11Apply new Configuration? (y/n) [n] : y
    12Applying... Done!
    13-I- Please reboot machine to load new configurations.
    
  7. 在两个虚拟机上的 CX-6 上都启用 ATS 后,将主机置于维护模式并重新启动 ESXi 主机。

    注意

    如果您在两台主机之间配置了 vMotion,则主机上的虚拟机可以在主机重新启动以启用 ATS 时移动到另一台正在运行的主机。

    注意

    请记住重新提交命令以在 PCIe 交换机上启用 ACS 直接转换位。

  8. ESXi 主机重新启动完成后,重新启动 vCenter 和虚拟机。

  9. 接下来,通过运行以下命令验证虚拟机上是否已启用 ATS

    1sudo mst start
    2sudo mlxconfig -d /dev/mst/mt4123_pciconf0 query | grep -i ATS
    3sudo lspci -vvv
    
  10. 搜索 Mellanox CX-6 设备,并验证输出是否包含如下配置的 ATS Capability

    1Capabilities: [480 v1] Address Translation Service (ATS)
    2    ATSCap: Invalidate Queue Depth: 00
    3     ATSCtl: Enable+, Smallest Translation Unit: 00
    

    注意

    Enable+ 表示已成功启用。

配置虚拟机的 NUMA 亲缘性#

  1. 检查您的网卡和 GPU 连接到哪个 NUMA 节点,在 ESXi 主机上运行以下命令

    1esxcli hardware pci list | grep -A 30 -B 10 NVIDIA
    2esxcli hardware pci list | grep -A 30 -B 10 Mellanox
    
  2. 以下输出描述了设备的 NUMA 节点

     10000:3b:02.3
     2    Address: 0000:3b:02.3
     3    Segment: 0x0000
     4    Bus: 0x3b
     5    Slot: 0x02
     6    Function: 0x3
     7    VMkernel Name: PF_0.59.0_VF_15
     8    Vendor Name: NVIDIA Corporation
     9    Device Name: NVIDIAA100-PCIE-40GB
    10    Configured Owner: VMkernel
    11    Current Owner: VMkernel
    12    Vendor ID: 0x10de
    13    Device ID: 0x20f1
    14    SubVendor ID: 0x10de
    15    SubDevice ID: 0x0000
    16    Device Class: 0x0302
    17    Device Class Name: 3D controller
    18    Programming Interface: 0x00
    19    Revision ID: 0xa1
    20    Interrupt Line: 0xff
    21    IRQ: 255
    22    Interrupt Vector: 0x00
    23PCI Pin: 0xff
    24    Spawned Bus: 0x00
    25    Flags: 0x0001
    26    Module ID: 54
    27    Module Name: nvidia
    28    Chassis: 0
    29    Physical Slot: -1
    30    Slot Description:
    31    Device Layer Bus Address: s00000001.00.vf15
    32    Passthru Capable: true
    33    Parent Device: PCI 0:58:0:0
    34    Dependent Device: PCI 0:59:2:3
    35    Reset Method: Function reset
    36    FPT Sharable: true
    37    NUMA Node: 0
    38    Extended Device ID: 65535
    39    Extended Device Name:
    
  3. 确保网卡和 GPU 位于同一 NUMA 节点上。

  4. 在 VM 配置中,添加新的键值对

    numa.nodeAffinity = <numa node value>
    

创建用于多节点训练的 Docker 文件#

  1. 按照以下 Dockerfile 创建 Docker 镜像

     1FROM nvcr.io/nvaie/tensorflow:21.07-tf1-py3
     2
     3ARG DEBIAN_FRONTEND=noninteractiv
     4
     5# Set MOFED version, OS version and platform
     6ENV MOFED_VERSION 5.2-2.2.4.0
     7
     8#http://content.mellanox.com/ofed/MLNX_OFED-5.2-2.2.4.0/MLNX_OFED_LINUX-5.2-2.2.4.0-ubuntu20.04-x86_64.tgz
     9ENV OS_VERSION ubuntu20.04
    10
    11ENV PLATFORM x86_64
    12
    13
    14RUN pip3 install --user --upgrade pip && \
    15    pip3 install --no-cache-dir absl-py
    16
    17RUN apt-get update && \
    18    apt-get install -y --allow-downgrades --allow-change-held-packages --no-install-recommends \
    19        apt-utils build-essential cmake tcsh tcl tk \
    20        make git curl vim wget ca-certificates \
    21        iputils-ping net-tools ethtool \
    22        perl lsb-release python-libxml2 \
    23        iproute2 pciutils libnl-route-3-200 \
    24        kmod libnuma1 lsof openssh-server \
    25        swig libelf1 automake libglib2.0-0 \
    26        autoconf graphviz chrpath flex libnl-3-200 m4 \
    27        debhelper autotools-dev gfortran libltdl-dev  \
    28        dmidecode build-essential cmake git zip pciutils hwloc  numactl \
    29        dpatch bison pkg-config numactl  dkms udev libnl-route-3-dev libnl-3-dev  \
    30        libmnl0 libmnl-dev expect-dev ncat \
    31        usbutils iperf3 bc tree \
    32        quilt  \
    33        landscape-common  libpci-dev && \
    34        rm -rf /var/lib/apt/lists/*
    35# hugepages libgfortran3 netcat
    36# linux-headers-$(uname -r)
    37
    38
    39WORKDIR /workspace
    40RUN wget http://content.mellanox.com/ofed/MLNX_OFED-${MOFED_VERSION}/MLNX_OFED_LINUX-$MOFED_VERSION-$OS_VERSION-$PLATFORM.tgz && \
    41    tar -xvf MLNX_OFED_LINUX-${MOFED_VERSION}-${OS_VERSION}-${PLATFORM}.tgz && \
    42    MLNX_OFED_LINUX-${MOFED_VERSION}-${OS_VERSION}-${PLATFORM}/mlnxofedinstall --user-space-only --without-fw-update --force && \
    43    tree /workspace/MLNX_OFED_LINUX-${MOFED_VERSION}-${OS_VERSION}-${PLATFORM}/
    44    #dpkg -i /workspace/MLNX_OFED_LINUX-${MOFED_VERSION}-${OS_VERSION}-${PLATFORM}/DEBS/libibumad-dev*.deb && \
    45    #dpkg -i /workspace/MLNX_OFED_LINUX-${MOFED_VERSION}-${OS_VERSION}-${PLATFORM}/DEBS/libibumad3*.deb
    46
    47
    48#    MLNX_OFED_LINUX-${MOFED_VERSION}-${OS_VERSION}-${PLATFORM}/mlnxofedinstall --dpdk --upstream-libs --without-fw-update --force --umad-dev-rw -q
    49#--user-space-only
    50#    MLNX_OFED_LINUX-${MOFED_VERSION}-${OS_VERSION}-${PLATFORM}/mlnxofedinstall --dpdk --without-fw-update --force  -q
    51
    52#WORKDIR /workspace
    53#RUN wget https://www.mellanox.com/downloads/MFT/mft-4.16.1-9-x86_64-deb.tgz && \
    54#tar xzvf mft-4.16.1-9-x86_64-deb.tgz&& \
    55#cd mft-4.16.1-9-x86_64-deb && \
    56#./install.sh
    57
    58
    59WORKDIR /workspace
    60RUN git clone -b cnn_tf_v1.15_compatible https://github.com/tensorflow/benchmarks.git
    61
    62
    63WORKDIR /workspace
    64RUN git clone https://github.com/NVIDIA/nccl-tests && \
    65cd nccl-tests && \
    66make MPI=1 MPI_HOME=/usr/local/mpi
    67
    68
    69WORKDIR /workspace
    70RUN git clone https://github.com/linux-rdma/perftest && \
    71    cd perftest && \
    72    ./autogen.sh && \
    73    CUDA_H_PATH=/usr/local/cuda/include/cuda.h ./configure && \
    74    make install
    75
    76
    77
    78WORKDIR /test
    79
    80
    81RUN rm -f ${_CUDA_COMPAT_PATH}/.*.checked
    
  2. 运行以下命令,在与 Dockerfile 相同的文件夹中构建 docker 多节点容器

    sudo docker build -t multinode:latest .
    
  3. 标记镜像并将其上传到您的 NVIDIA AI Enterprise 私有注册表

    1sudo docker tag multinode <NVIDIA_AI_Enterprise_private_registry_username>/multinode
    2sudo docker push
    

在多节点集群上的虚拟机之间设置无密钥登录#

在全新安装的系统上,~/.ssh 目录通常为空。但是,将使用本指南中的步骤生成/添加以下文件

id_rsaid_rsa.pub

用于节点之间无密钥登录的 SSH 密钥。

authorized_keys

服务器识别的来自其他节点/系统的 RSA 公钥列表,用于 ssh 访问。

config

创建的文件,用于在访问其他节点时提供 ssh 安全密钥检查设置。

mpicont.sh

我们将创建的脚本,用于允许 mpi 在不同节点上的容器之间通信。

ssh_container/

一个目录,包含上述文件,但用于节点间容器通信。

known_hosts

此文件由 ssh 自动生成,并列出用户曾经连接的所有主机的密钥。

生成 SSH 密钥#

在主节点上,我们将创建一对在节点之间共享的 ssh 密钥。然后将生成另一对密钥,用于在节点之间运行的容器之间使用。在本指南中,我们将相应地命名每组密钥,但默认密钥名称 id_rsaid_rsa.pub 也可以。

主机/工作节点 SSH 密钥#

  1. 在命令行终端中,创建一个新的 SSH 密钥

    ssh-keygen -t rsa
    
  2. 输入要保存密钥的文件 (/home/nvidia/.ssh/id_rsa)

    id_rsa_host
    

这将生成以下文件

  • id_rsa_host

  • id_rsa_host.pub

容器 SSH 密钥#

  1. 创建一个名为 ssh_container 的目录。此目录可以创建在任何位置,但在此示例中,我们将其放在 ~/.ssh 目录中

    1mkdir ssh_container
    2cd ssh_container
    3ssh-keygen -t rsa
    
  2. 输入要保存密钥的文件 (/home/nvidia/.ssh/id_rsa)

    <path/to>/ssh_container/id_rsa_cont
    

ssh_container 目录中,这将生成

  • id_rsa_cont

  • id_rsa_cont.pub

创建用于无密钥登录的配置文件#

在我们的实验室环境中,Ubuntu 虚拟机的用户名是 nvidia。请在以下步骤中替换用户名以反映您环境中的用户。在主节点上,创建一个名为 config 的文件 (~/.ssh/config),并放入以下内容

1Host *
2    User nvidia
3    IdentityFile ~/.ssh/id_rsa_host
4    StrictHostKeyChecking no
5    UserKnownHostsFile=/dev/null

ssh_container 目录 (~/.ssh/ssh_container/config) 中,为容器之间的无密钥登录创建另一个 config 文件

1Host *
2    User nvidia
3    IdentityFile /root/.ssh/id_rsa_cont
4    StrictHostKeyChecking no
5    UserKnownHostsFile=/dev/null
6    LogLevel=Error
7    ServerAliveInterval=30

将公共 SSH 密钥添加到“Authorized_keys”#

为了使无密钥登录在工作节点上工作,需要将公共 ssh 密钥的内容复制到 authorized_keys 文件中,用于节点间通信和不同节点上容器之间的通信。

~/.ssh 文件夹中

echo `cat id_rsa_host.pub` > authorized_keys

~/.ssh/ssh_container 文件夹中

echo `cat id_rsa_cont.pub` > authorized_keys

创建 mpicont.sh 脚本#

  1. ~/.ssh 目录中,创建一个名为 mpicont.sh 的脚本,内容如下

    1mpicont.sh
    2docker exec mpicont /bin/bash -c "$SSH_ORIGINAL_COMMAND"
    
  2. 然后使脚本可执行

    chmod +x mpicont.sh
    
将容器 SSH 密钥添加到主节点的 authorized_keys 文件#

将以下行添加到主 authorized_keys 文件

command="bash /home/nvidia/.ssh/mpicont.sh",no-port-forwarding,no-agent-forwarding,no-X11-forwarding <add contents of id_rsa_cont.pub>
~/.ssh 复制到工作节点并确认无密钥登录#

现在我们可以将主节点的 ~/.ssh 目录中的所有文件复制到我们在节点列表中指定的所有工作节点。

scp -r .ssh $<worker_node_IP>:/home/nvidia/.ssh/;done
更改所有节点上 ssh_container 中的权限#

在所有节点上,更改 ssh_container/config 文件的所有权,使所有者为 root

sudo chown root:root config

然后将 ssh_container 文件夹中所有文件的权限更改为 600。

sudo chmod 600 *

以下是复制到工作节点的所有文件及其正确权限的列表

1~/.ssh$ ll *
2-rw------- 1 nvidia nvidia  894 Jan 24 17:46 authorized_keys
3-rw-r--r-- 1 nvidia nvidia  125 Jan 24 14:21 config
4-rw------- 1 nvidia nvidia 1675 Jan 24 14:19 id_rsa_host
5-rw-r--r-- 1 nvidia nvidia  396 Jan 24 14:19 id_rsa_host.pub
6-rwxrwxr-x 1 nvidia nvidia   57 Jan 24 15:55 mpicont.sh*

ssh_container:

1total 24
2drwxrwxr-x 2 nvidia nvidia 4096 Feb  6 16:50 ./
3drwxrwxr-x 4 nvidia nvidia 4096 Feb  7 11:29 ../
4-rw------- 1 nvidia nvidia  396 Jan 24 15:58 authorized_keys
5-rw------- 1 root   root    161 Jan 24 17:54 config
6-rw------- 1 nvidia nvidia 1675 Jan 24 15:58 id_rsa_cont
7-rw------- 1 nvidia nvidia  396 Jan 24 15:58 id_rsa_cont.pub

现在在所有工作节点上运行 Docker 容器,使用以下命令

sudo docker run -it --gpus=all --net=host --uts=host --ipc=host --ulimit stack=67108864 --ulimit memlock=-1 --shm-size=1g --name=mpicont --device=/dev/infiniband -v /home/nvidia/.ssh/ssh_container:/root/.ssh <NVIDIA_AI_Enterprise_private_registry_username>/multinode:latest sleep infinity

在主节点上,运行

sudo docker run -it --gpus=all --net=host --uts=host --ipc=host --ulimit stack=67108864 --ulimit memlock=-1 --shm-size=1g --name=mpicont --device=/dev/infiniband -v /home/nvidia/.ssh/ssh_container:/root/.ssh <NVIDIA_AI_Enterprise_private_registry_username>/multinode:latest /bin/bash

要测试 ssh 无密钥 mpi 命令是否正在运行,请根据您拥有的工作节点数量运行以下命令

mpirun --allow-run-as-root -H <master_IP>,<worker1_IP>,<worker2_IP>,<worker3_IP> -np "4" hostname

要验证所有工作节点上的可用 GPU,请运行以下命令

mpirun --allow-run-as-root -H <worker1_IP>,<worker2_IP>,<worker3_IP> -np "3" nvidia-smi

注意

在我们的实验室环境中,np(进程数,或者换句话说,GPU 数量)参数为 4。请修改 np 参数以反映您的环境。

输出应反映所有四个节点的主机名。

安装 nv_peer_memory#

在每个节点上安装 nv_peer_mem 模块。

git clone https://github.com/Mellanox/nv_peer_memory.git
cd nv_peer_memory
./build_module.sh
cd /tmp
tar xzf /tmp/nvidia-peer-memory_1.0.orig.tar.gz
cd nvidia-peer-memory-1.0
dpkg-buildpackage -us -uc
dpkg -i <path to generated deb files>

运行 ResNet-50 多节点训练示例#

注意

确保 ssh 无密钥 mpi 正在运行,命令如下

mpirun --allow-run-as-root -H <master_IP>,<worker1_IP>,<worker2_IP>,<worker3_IP> -np "4" hostname

运行以下命令以测试 ResNet-50 多节点基准测试示例,具体取决于工作节点计数

mpirun --allow-run-as-root -H <master_IP>,<worker1_IP>,<worker2_IP>,<worker3_IP>  -np "4 " -x NCCL_IB_DISABLE=0 -x NCCL_DEBUG=INFO  python3 /workspace/benchmarks/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --model resnet50 --batch_size 512 --use_fp16 --variable_update=horovod --xla=True

解释结果#

此基准测试报告每次报告迭代的每秒图像数训练性能。使用最后几个报告的值来表示训练性能。

 1Done warm up
 2Step        Img/sec total_loss
 3Done warm up
 4Step        Img/sec total_loss
 5Done warm up
 6Step        Img/sec total_loss
 7Done warm up
 8Step        Img/sec total_loss
 91   images/sec: 2100.6 +/- 0.0 (jitter = 0.0)       7.738
101   images/sec: 2100.8 +/- 0.0 (jitter = 0.0)       7.742
111   images/sec: 2100.2 +/- 0.0 (jitter = 0.0)       7.734
121   images/sec: 2100.8 +/- 0.0 (jitter = 0.0)       7.770
1310  images/sec: 2100.0 +/- 61.9 (jitter = 6.6)      7.607
1410  images/sec: 2100.4 +/- 60.4 (jitter = 189.7)    7.656
1510  images/sec: 2100.9 +/- 59.2 (jitter = 88.7)     7.611
1610  images/sec: 2100.9 +/- 59.0 (jitter = 175.8)    7.647
1720  images/sec: 2100.2 +/- 39.4 (jitter = 92.3)     7.527
1820  images/sec: 2100.2 +/- 43.8 (jitter = 198.3)    7.515
1920  images/sec: 2100.1 +/- 41.1 (jitter = 181.8)    7.512
2020  images/sec: 2100.1 +/- 43.0 (jitter = 14.7)     7.501
2130  images/sec: 2100.9 +/- 34.9 (jitter = 198.3)    7.490
2230  images/sec: 2100.4 +/- 35.3 (jitter = 11.1)     7.474
2330  images/sec: 2100.7 +/- 33.3 (jitter = 92.9)     7.483
2430  images/sec: 2100.3 +/- 34.9 (jitter = 157.3)    7.493
2540  images/sec: 2100.5 +/- 28.3 (jitter = 76.4)     7.476
2640  images/sec: 2100.9 +/- 31.2 (jitter = 193.8)    7.476
2740  images/sec: 2100.5 +/- 31.2 (jitter = 186.9)    7.483
2840  images/sec: 2100.2 +/- 31.5 (jitter = 18.9)     7.474
2950  images/sec: 2100.8 +/- 28.1 (jitter = 15.0)     7.480
3050  images/sec: 2100.3 +/- 28.3 (jitter = 168.8)    7.468
3150  images/sec: 2100.7 +/- 25.7 (jitter = 76.4)     7.485
3250  images/sec: 2100.2 +/- 27.4 (jitter = 218.1)    7.485
3360  images/sec: 2100.2 +/- 25.6 (jitter = 173.0)    7.485
3460  images/sec: 2100.3 +/- 23.3 (jitter = 66.1)     7.501
3560  images/sec: 2100.4 +/- 24.8 (jitter = 190.7)    7.480
3660  images/sec: 2100.2 +/- 26.4 (jitter = 20.6)     7.493
3770  images/sec: 2100.4 +/- 24.3 (jitter = 16.4)     7.495
3870  images/sec: 2100.4 +/- 23.9 (jitter = 157.3)    7.498
3970  images/sec: 2100.0 +/- 22.1 (jitter = 52.3)     7.503
4070  images/sec: 2100.5 +/- 23.4 (jitter = 218.3)    7.509
4180  images/sec: 2100.3 +/- 22.4 (jitter = 157.3)    7.490
4280  images/sec: 2100.2 +/- 20.6 (jitter = 50.7)     7.510
4380  images/sec: 2100.6 +/- 21.7 (jitter = 195.2)    7.520
4480  images/sec: 2100.2 +/- 22.4 (jitter = 30.3)     7.508
4590  images/sec: 2100.8 +/- 21.2 (jitter = 22.3)     7.481
4690  images/sec: 2100.1 +/- 20.8 (jitter = 157.3)    7.489
4790  images/sec: 2100.7 +/- 19.7 (jitter = 35.1)     7.496
4890  images/sec: 2100.7 +/- 20.7 (jitter = 218.1)    7.471
49100 images/sec: 2100.2 +/- 20.2 (jitter = 30.3)     7.501
50----------------------------------------------------------------
51total images/sec: 8400.46
52----------------------------------------------------------------
53100 images/sec: 1520.1 +/- 19.9 (jitter = 166.6)    7.522
54----------------------------------------------------------------
55total images/sec: 8400.99
56----------------------------------------------------------------
57100 images/sec: 1517.6 +/- 18.6 (jitter = 52.3)     7.507
58----------------------------------------------------------------
59total images/sec: 8400.84
60----------------------------------------------------------------
61100 images/sec: 1517.9 +/- 19.6 (jitter = 219.0)    7.500
62----------------------------------------------------------------
63total images/sec: 8400.58
64----------------------------------------------------------------