We’re comparing performance of the apps.mellanox.connectx driver between two systems:

BIOS settings unknown are at this point.

The configuration is one 2x100G port NIC, with both ports wired to each other. Packetblaster will transmit packets one one port in both test cases. In the Receive performance test case packets are received in the other port.

The code under test can be found here: eugeneia/mellanox-benchmark

We run benchmarks on a matrix of parameters including

The benchmark is run three times for every parameter configuration and we show the minimum, maximum, and average of the results.

We also overlay 100G linerate (grey dashed line) to put the results into perspective.

Gbps <- function(mpps,pktsize) {
  mpps*(12+8+pktsize)*8/1000
}

Linerate <- function(G, pktsize) {
  G*1e9 / ((12+8+pktsize)*8)
}

Packetblaster performance

Packetblaster is a optimized TX routine for our Connect-X driver. It should demonstrate the maximum transmit rate supported by the NIC.

This should reproduce the results in ConnectX: Review N*SQ 64B transmit performance mellanox (Rev 2) from 2016. It quite doesn’t though, and is not the same code. Have to investigate what’s the difference.

packetblaster <- (mellanox.tx.only.queues.sizes.intel.100e6.coarse %>%
           mutate(system="Intel Xeon Silver 4116 @ 2.10GHz")) %>%
  bind_rows((mellanox.tx.only.queues.sizes.epyc.100e6.coarse %>%
               mutate(system="AMD EPYC 7443P"))) %>%
  mutate(workers=sprintf("%d workers (cores)", workers),
         queues=sprintf("%d queues", queues)) %>%
  group_by(system, workers, queues, pktsize) %>% 
  summarise(min_mpps=min(rate), avg_mpps=mean(rate), max_mpps=max(rate),
            min_loss=(min(drop+error)), min_loss=(mean(drop+error)), max_loss=(max(drop+error))) %>%
  ungroup() %>%
  mutate(Gbps=Gbps(max_mpps-max_loss, pktsize))
`summarise()` has grouped output by 'system', 'workers', 'queues'. You can override using the `.groups` argument.
ggplot(packetblaster, aes(x=pktsize, color=queues)) +
  facet_grid(system ~ workers) + 
  geom_line(aes(y=max_mpps, linetype="0_tx")) +
  geom_line(aes(y=max_loss, linetype="1_loss")) + 
  geom_point(aes(y=avg_mpps, shape="avg"), alpha=0.5) +
  geom_point(aes(y=max_mpps, shape="max"), alpha=0.5) +
  geom_point(aes(y=min_mpps, shape="min"), alpha=0.5) +
  geom_line(aes(y=Linerate(100, pktsize)/1e6, linetype="2_linerate"), color='grey') +
  coord_cartesian(ylim=c(NA, max(packetblaster$max_mpps))) +
  ggtitle("Multi core performance by number of queues per worker and packet size",
          subtitle="apps.mellanox.connectx: Packetblaster rate MPPS (TX only)")

Receive performance

Packetblaster transmits on one port, and packets are received on the other port. Each side has a dedicated CPU core for each worker. I.e., “6 workers” means six cores used for transmit, and six distinct cores are used for receive.

txrx <- (mellanox.tx.rx.queues.sizes.intel.100e6.coarse %>%
          mutate(system="Intel Xeon Silver 4116 @ 2.10GHz")) %>%
  bind_rows((mellanox.tx.rx.queues.sizes.epyc.100e6.coarse %>%
               mutate(system="AMD EPYC 7443P") %>% na.omit())) %>%
  mutate(workers=sprintf("%d workers (cores)", workers),
         queues=sprintf("%d queues", queues)) %>%
  mutate(rx_mpps=rxrate-(rxdrop+rxerror)) %>%
  group_by(system, workers, queues, pktsize) %>% 
  summarise(min_mpps=min(txrate),
            avg_mpps=mean(txrate),
            max_mpps=max(txrate),
            min_rx_mpps=(min(rx_mpps)),
            avg_rx_mpps=(mean(rx_mpps)),
            max_rx_mpps=(max(rx_mpps))) %>%
  ungroup() %>%
  mutate(rxGbps=Gbps(max_rx_mpps, pktsize), Gbps=Gbps(max_mpps, pktsize))
`summarise()` has grouped output by 'system', 'workers', 'queues'. You can override using the `.groups` argument.
ggplot(txrx, aes(x=pktsize, color=queues)) +
  facet_grid(system ~ workers) +
  geom_line(aes(y=max_rx_mpps, linetype="0_rx")) +
  geom_line(aes(y=max_mpps, linetype="1_tx")) + 
  geom_point(aes(y=avg_rx_mpps, shape="avg"), alpha=0.5) +
  geom_point(aes(y=max_rx_mpps, shape="max"), alpha=0.5) +
  geom_point(aes(y=min_rx_mpps, shape="min"), alpha=0.5) +
  geom_line(aes(y=Linerate(100, pktsize)/1e6, linetype="2_linerate"), color='grey') +
  coord_cartesian(ylim=c(NA, max(txrx$max_mpps))) +
  ggtitle("Multi core performance by number of queues per worker and packet size",
          subtitle="apps.mellanox.connectx: RX rate of combined receive queues in MPPS")

Zoom into results on EPYC

Packetblaster

packetblaster_epyc <- filter(packetblaster, system=="AMD EPYC 7443P")
ggplot(packetblaster_epyc, aes(x=pktsize, color=queues)) +
  facet_grid(system ~ workers) + 
  geom_line(aes(y=max_mpps, linetype="0_tx")) +
  geom_line(aes(y=max_loss, linetype="1_loss")) + 
  geom_point(aes(y=avg_mpps, shape="avg"), alpha=0.5) +
  geom_point(aes(y=max_mpps, shape="max"), alpha=0.5) +
  geom_point(aes(y=min_mpps, shape="min"), alpha=0.5) +
  geom_line(aes(y=Linerate(100, pktsize)/1e6, linetype="2_linerate"), color='grey') +
  coord_cartesian(ylim=c(NA, max(packetblaster_epyc$max_mpps))) +
  ggtitle("Multi core performance by number of queues per worker and packet size",
          subtitle="apps.mellanox.connectx: Packetblaster rate MPPS (TX only)")

Receive

txrx_epyc <- filter(txrx, system=="AMD EPYC 7443P")
ggplot(txrx_epyc, aes(x=pktsize, color=queues)) +
  facet_grid(system ~ workers) +
  geom_line(aes(y=max_rx_mpps, linetype="0_rx")) +
  geom_line(aes(y=max_mpps, linetype="1_tx")) + 
  geom_point(aes(y=avg_rx_mpps, shape="avg"), alpha=0.5) +
  geom_point(aes(y=max_rx_mpps, shape="max"), alpha=0.5) +
  geom_point(aes(y=min_rx_mpps, shape="min"), alpha=0.5) +
  geom_line(aes(y=Linerate(100, pktsize)/1e6, linetype="2_linerate"), color='grey') +
  coord_cartesian(ylim=c(NA, max(txrx_epyc$max_mpps))) +
  ggtitle("Multi core performance by number of queues per worker and packet size",
          subtitle="apps.mellanox.connectx: RX rate of combined receive queues in MPPS")

RX Drops

ggplot(txrx, aes(x=pktsize, color=queues)) +
  facet_grid(system ~ workers) +
  geom_line(aes(y=max_mpps-max_rx_mpps, linetype="0_drop")) +
  geom_line(aes(y=max_mpps, linetype="1_tx")) + 
  geom_point(aes(y=avg_mpps-avg_rx_mpps, shape="avg"), alpha=0.5) +
  geom_point(aes(y=max_mpps-max_rx_mpps, shape="max"), alpha=0.5) +
  geom_point(aes(y=min_mpps-min_rx_mpps, shape="min"), alpha=0.5) +
  geom_line(aes(y=Linerate(100, pktsize)/1e6, linetype="2_linerate"), color='grey') +
  coord_cartesian(ylim=c(NA, max(txrx$max_mpps))) +
  ggtitle("Multi core performance by number of queues per worker and packet size",
          subtitle="apps.mellanox.connectx: Loss rate of combined receive queues in MPPS")

Help

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tDQp0aXRsZTogImFwcHMubWVsbGFub3guY29ubmVjdHg6IFBhY2tldGJsYXN0ZXIgYW5kIFJlY2VpdmUgcGVyZm9ybWFuY2UiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpXZeKAmXJlIGNvbXBhcmluZyBwZXJmb3JtYW5jZSBvZiB0aGUgYGFwcHMubWVsbGFub3guY29ubmVjdHhgIGRyaXZlciBiZXR3ZWVuIHR3bw0Kc3lzdGVtczoNCg0KIC0gYEFNRCBFUFlDIDc0NDNQYCwgYE1lbGxhbm94IFRlY2hub2xvZ2llcyBNVDI4ODAwIEZhbWlseSBbQ29ubmVjdFgtNSBFeF1gDQogLSBgSW50ZWwgWGVvbiBTaWx2ZXIgNDExNiBAIDIuMTBHSHpgLCBgTWVsbGFub3ggVGVjaG5vbG9naWVzIE1UMjc4MDAgRmFtaWx5IFtDb25uZWN0WC01XWANCg0KQklPUyBzZXR0aW5ncyB1bmtub3duIGFyZSBhdCB0aGlzIHBvaW50Lg0KDQpUaGUgY29uZmlndXJhdGlvbiBpcyBvbmUgMngxMDBHIHBvcnQgTklDLCB3aXRoIGJvdGggcG9ydHMgd2lyZWQgdG8gZWFjaCBvdGhlci4NClBhY2tldGJsYXN0ZXIgd2lsbCB0cmFuc21pdCBwYWNrZXRzIG9uZSBvbmUgcG9ydCBpbiBib3RoIHRlc3QgY2FzZXMuDQpJbiB0aGUgKlJlY2VpdmUgcGVyZm9ybWFuY2UqIHRlc3QgY2FzZSBwYWNrZXRzIGFyZSByZWNlaXZlZCBpbiB0aGUgb3RoZXIgcG9ydC4NCg0KVGhlIGNvZGUgdW5kZXIgdGVzdCBjYW4gYmUgZm91bmQgaGVyZToNCltldWdlbmVpYS9tZWxsYW5veC1iZW5jaG1hcmtdKGh0dHBzOi8vZ2l0aHViLmNvbS9ldWdlbmVpYS9zbmFiYi9jb21taXRzL21lbGxhbm94LWJlbmNobWFyaykNCg0KV2UgcnVuIGJlbmNobWFya3Mgb24gYSBtYXRyaXggb2YgcGFyYW1ldGVycyBpbmNsdWRpbmcNCg0KLSBwYWNrZXQgc2l6ZSAoaW5jbHVkaW5nIDQtYnl0ZSBDUkMpDQotIG51bWJlciBvZiB3b3JrZXJzIChjcHUgY29yZXMpIHByb3ZpZGVkIHRvIHRoZSBhcHBsaWNhdGlvbg0KLSBudW1iZXIgb2YgaGFyZHdhcmUgc2VuZC9yZWNlaXZlIHF1ZXVlcyBwZXIgd29ya2VyDQoNClRoZSBiZW5jaG1hcmsgaXMgcnVuIHRocmVlIHRpbWVzIGZvciBldmVyeSBwYXJhbWV0ZXIgY29uZmlndXJhdGlvbg0KYW5kIHdlIHNob3cgdGhlIG1pbmltdW0sIG1heGltdW0sIGFuZCBhdmVyYWdlIG9mIHRoZSByZXN1bHRzLg0KDQpXZSBhbHNvIG92ZXJsYXkgMTAwRyBsaW5lcmF0ZSAoZ3JleSBkYXNoZWQgbGluZSkgdG8gcHV0IHRoZSByZXN1bHRzIGludG8NCnBlcnNwZWN0aXZlLg0KDQpgYGB7cn0NCkdicHMgPC0gZnVuY3Rpb24obXBwcyxwa3RzaXplKSB7DQogIG1wcHMqKDEyKzgrcGt0c2l6ZSkqOC8xMDAwDQp9DQoNCkxpbmVyYXRlIDwtIGZ1bmN0aW9uKEcsIHBrdHNpemUpIHsNCiAgRyoxZTkgLyAoKDEyKzgrcGt0c2l6ZSkqOCkNCn0NCmBgYA0KDQojIFBhY2tldGJsYXN0ZXIgcGVyZm9ybWFuY2UNCg0KUGFja2V0Ymxhc3RlciBpcyBhIG9wdGltaXplZCBUWCByb3V0aW5lIGZvciBvdXIgQ29ubmVjdC1YIGRyaXZlci4NCkl0IHNob3VsZCBkZW1vbnN0cmF0ZSB0aGUgbWF4aW11bSB0cmFuc21pdCByYXRlIHN1cHBvcnRlZCBieSB0aGUgTklDLg0KDQpUaGlzIHNob3VsZCByZXByb2R1Y2UgdGhlIHJlc3VsdHMgaW4gW0Nvbm5lY3RYOiBSZXZpZXcgTipTUSA2NEIgdHJhbnNtaXQgcGVyZm9ybWFuY2UgbWVsbGFub3ggKFJldiAyKV0oaHR0cHM6Ly9naXRodWIuY29tL3NuYWJiY28vc25hYmIvaXNzdWVzLzEwMDcpDQpmcm9tIDIwMTYuIEl0IHF1aXRlIGRvZXNu4oCZdCB0aG91Z2gsIGFuZCBpcyBub3QgdGhlIHNhbWUgY29kZS4gSGF2ZSB0byBpbnZlc3RpZ2F0ZQ0Kd2hhdOKAmXMgdGhlIGRpZmZlcmVuY2UuDQoNCmBgYHtyfQ0KcGFja2V0Ymxhc3RlciA8LSAobWVsbGFub3gudHgub25seS5xdWV1ZXMuc2l6ZXMuaW50ZWwuMTAwZTYuY29hcnNlICU+JQ0KICAgICAgICAgICBtdXRhdGUoc3lzdGVtPSJJbnRlbCBYZW9uIFNpbHZlciA0MTE2IEAgMi4xMEdIeiIpKSAlPiUNCiAgYmluZF9yb3dzKChtZWxsYW5veC50eC5vbmx5LnF1ZXVlcy5zaXplcy5lcHljLjEwMGU2LmNvYXJzZSAlPiUNCiAgICAgICAgICAgICAgIG11dGF0ZShzeXN0ZW09IkFNRCBFUFlDIDc0NDNQIikpKSAlPiUNCiAgbXV0YXRlKHdvcmtlcnM9c3ByaW50ZigiJWQgd29ya2VycyAoY29yZXMpIiwgd29ya2VycyksDQogICAgICAgICBxdWV1ZXM9c3ByaW50ZigiJWQgcXVldWVzIiwgcXVldWVzKSkgJT4lDQogIGdyb3VwX2J5KHN5c3RlbSwgd29ya2VycywgcXVldWVzLCBwa3RzaXplKSAlPiUgDQogIHN1bW1hcmlzZShtaW5fbXBwcz1taW4ocmF0ZSksIGF2Z19tcHBzPW1lYW4ocmF0ZSksIG1heF9tcHBzPW1heChyYXRlKSwNCiAgICAgICAgICAgIG1pbl9sb3NzPShtaW4oZHJvcCtlcnJvcikpLCBtaW5fbG9zcz0obWVhbihkcm9wK2Vycm9yKSksIG1heF9sb3NzPShtYXgoZHJvcCtlcnJvcikpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoR2Jwcz1HYnBzKG1heF9tcHBzLW1heF9sb3NzLCBwa3RzaXplKSkNCmBgYA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0NCmdncGxvdChwYWNrZXRibGFzdGVyLCBhZXMoeD1wa3RzaXplLCBjb2xvcj1xdWV1ZXMpKSArDQogIGZhY2V0X2dyaWQoc3lzdGVtIH4gd29ya2VycykgKyANCiAgZ2VvbV9saW5lKGFlcyh5PW1heF9tcHBzLCBsaW5ldHlwZT0iMF90eCIpKSArDQogIGdlb21fbGluZShhZXMoeT1tYXhfbG9zcywgbGluZXR5cGU9IjFfbG9zcyIpKSArIA0KICBnZW9tX3BvaW50KGFlcyh5PWF2Z19tcHBzLCBzaGFwZT0iYXZnIiksIGFscGhhPTAuNSkgKw0KICBnZW9tX3BvaW50KGFlcyh5PW1heF9tcHBzLCBzaGFwZT0ibWF4IiksIGFscGhhPTAuNSkgKw0KICBnZW9tX3BvaW50KGFlcyh5PW1pbl9tcHBzLCBzaGFwZT0ibWluIiksIGFscGhhPTAuNSkgKw0KICBnZW9tX2xpbmUoYWVzKHk9TGluZXJhdGUoMTAwLCBwa3RzaXplKS8xZTYsIGxpbmV0eXBlPSIyX2xpbmVyYXRlIiksIGNvbG9yPSdncmV5JykgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKE5BLCBtYXgocGFja2V0Ymxhc3RlciRtYXhfbXBwcykpKSArDQogIGdndGl0bGUoIk11bHRpIGNvcmUgcGVyZm9ybWFuY2UgYnkgbnVtYmVyIG9mIHF1ZXVlcyBwZXIgd29ya2VyIGFuZCBwYWNrZXQgc2l6ZSIsDQogICAgICAgICAgc3VidGl0bGU9ImFwcHMubWVsbGFub3guY29ubmVjdHg6IFBhY2tldGJsYXN0ZXIgcmF0ZSBNUFBTIChUWCBvbmx5KSIpDQpgYGANCg0KIyBSZWNlaXZlIHBlcmZvcm1hbmNlDQoNClBhY2tldGJsYXN0ZXIgdHJhbnNtaXRzIG9uIG9uZSBwb3J0LCBhbmQgcGFja2V0cyBhcmUgcmVjZWl2ZWQgb24gdGhlIG90aGVyIHBvcnQuDQpFYWNoIHNpZGUgaGFzIGEgZGVkaWNhdGVkIENQVSBjb3JlIGZvciBlYWNoIHdvcmtlci4NCkkuZS4sICI2IHdvcmtlcnMiIG1lYW5zIHNpeCBjb3JlcyB1c2VkIGZvciB0cmFuc21pdCwNCmFuZCBzaXggZGlzdGluY3QgY29yZXMgYXJlIHVzZWQgZm9yIHJlY2VpdmUuDQoNCmBgYHtyfQ0KdHhyeCA8LSAobWVsbGFub3gudHgucngucXVldWVzLnNpemVzLmludGVsLjEwMGU2LmNvYXJzZSAlPiUNCiAgICAgICAgICBtdXRhdGUoc3lzdGVtPSJJbnRlbCBYZW9uIFNpbHZlciA0MTE2IEAgMi4xMEdIeiIpKSAlPiUNCiAgYmluZF9yb3dzKChtZWxsYW5veC50eC5yeC5xdWV1ZXMuc2l6ZXMuZXB5Yy4xMDBlNi5jb2Fyc2UgJT4lDQogICAgICAgICAgICAgICBtdXRhdGUoc3lzdGVtPSJBTUQgRVBZQyA3NDQzUCIpICU+JSBuYS5vbWl0KCkpKSAlPiUNCiAgbXV0YXRlKHdvcmtlcnM9c3ByaW50ZigiJWQgd29ya2VycyAoY29yZXMpIiwgd29ya2VycyksDQogICAgICAgICBxdWV1ZXM9c3ByaW50ZigiJWQgcXVldWVzIiwgcXVldWVzKSkgJT4lDQogIG11dGF0ZShyeF9tcHBzPXJ4cmF0ZS0ocnhkcm9wK3J4ZXJyb3IpKSAlPiUNCiAgZ3JvdXBfYnkoc3lzdGVtLCB3b3JrZXJzLCBxdWV1ZXMsIHBrdHNpemUpICU+JSANCiAgc3VtbWFyaXNlKG1pbl9tcHBzPW1pbih0eHJhdGUpLA0KICAgICAgICAgICAgYXZnX21wcHM9bWVhbih0eHJhdGUpLA0KICAgICAgICAgICAgbWF4X21wcHM9bWF4KHR4cmF0ZSksDQogICAgICAgICAgICBtaW5fcnhfbXBwcz0obWluKHJ4X21wcHMpKSwNCiAgICAgICAgICAgIGF2Z19yeF9tcHBzPShtZWFuKHJ4X21wcHMpKSwNCiAgICAgICAgICAgIG1heF9yeF9tcHBzPShtYXgocnhfbXBwcykpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUocnhHYnBzPUdicHMobWF4X3J4X21wcHMsIHBrdHNpemUpLCBHYnBzPUdicHMobWF4X21wcHMsIHBrdHNpemUpKQ0KYGBgDQoNCg0KYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9DQpnZ3Bsb3QodHhyeCwgYWVzKHg9cGt0c2l6ZSwgY29sb3I9cXVldWVzKSkgKw0KICBmYWNldF9ncmlkKHN5c3RlbSB+IHdvcmtlcnMpICsNCiAgZ2VvbV9saW5lKGFlcyh5PW1heF9yeF9tcHBzLCBsaW5ldHlwZT0iMF9yeCIpKSArDQogIGdlb21fbGluZShhZXMoeT1tYXhfbXBwcywgbGluZXR5cGU9IjFfdHgiKSkgKyANCiAgZ2VvbV9wb2ludChhZXMoeT1hdmdfcnhfbXBwcywgc2hhcGU9ImF2ZyIpLCBhbHBoYT0wLjUpICsNCiAgZ2VvbV9wb2ludChhZXMoeT1tYXhfcnhfbXBwcywgc2hhcGU9Im1heCIpLCBhbHBoYT0wLjUpICsNCiAgZ2VvbV9wb2ludChhZXMoeT1taW5fcnhfbXBwcywgc2hhcGU9Im1pbiIpLCBhbHBoYT0wLjUpICsNCiAgZ2VvbV9saW5lKGFlcyh5PUxpbmVyYXRlKDEwMCwgcGt0c2l6ZSkvMWU2LCBsaW5ldHlwZT0iMl9saW5lcmF0ZSIpLCBjb2xvcj0nZ3JleScpICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YyhOQSwgbWF4KHR4cngkbWF4X21wcHMpKSkgKw0KICBnZ3RpdGxlKCJNdWx0aSBjb3JlIHBlcmZvcm1hbmNlIGJ5IG51bWJlciBvZiBxdWV1ZXMgcGVyIHdvcmtlciBhbmQgcGFja2V0IHNpemUiLA0KICAgICAgICAgIHN1YnRpdGxlPSJhcHBzLm1lbGxhbm94LmNvbm5lY3R4OiBSWCByYXRlIG9mIGNvbWJpbmVkIHJlY2VpdmUgcXVldWVzIGluIE1QUFMiKQ0KYGBgDQoNCg0KIyBab29tIGludG8gcmVzdWx0cyBvbiBFUFlDDQoNCiMjIFBhY2tldGJsYXN0ZXINCg0KYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9DQpwYWNrZXRibGFzdGVyX2VweWMgPC0gZmlsdGVyKHBhY2tldGJsYXN0ZXIsIHN5c3RlbT09IkFNRCBFUFlDIDc0NDNQIikNCmdncGxvdChwYWNrZXRibGFzdGVyX2VweWMsIGFlcyh4PXBrdHNpemUsIGNvbG9yPXF1ZXVlcykpICsNCiAgZmFjZXRfZ3JpZChzeXN0ZW0gfiB3b3JrZXJzKSArIA0KICBnZW9tX2xpbmUoYWVzKHk9bWF4X21wcHMsIGxpbmV0eXBlPSIwX3R4IikpICsNCiAgZ2VvbV9saW5lKGFlcyh5PW1heF9sb3NzLCBsaW5ldHlwZT0iMV9sb3NzIikpICsgDQogIGdlb21fcG9pbnQoYWVzKHk9YXZnX21wcHMsIHNoYXBlPSJhdmciKSwgYWxwaGE9MC41KSArDQogIGdlb21fcG9pbnQoYWVzKHk9bWF4X21wcHMsIHNoYXBlPSJtYXgiKSwgYWxwaGE9MC41KSArDQogIGdlb21fcG9pbnQoYWVzKHk9bWluX21wcHMsIHNoYXBlPSJtaW4iKSwgYWxwaGE9MC41KSArDQogIGdlb21fbGluZShhZXMoeT1MaW5lcmF0ZSgxMDAsIHBrdHNpemUpLzFlNiwgbGluZXR5cGU9IjJfbGluZXJhdGUiKSwgY29sb3I9J2dyZXknKSArDQogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoTkEsIG1heChwYWNrZXRibGFzdGVyX2VweWMkbWF4X21wcHMpKSkgKw0KICBnZ3RpdGxlKCJNdWx0aSBjb3JlIHBlcmZvcm1hbmNlIGJ5IG51bWJlciBvZiBxdWV1ZXMgcGVyIHdvcmtlciBhbmQgcGFja2V0IHNpemUiLA0KICAgICAgICAgIHN1YnRpdGxlPSJhcHBzLm1lbGxhbm94LmNvbm5lY3R4OiBQYWNrZXRibGFzdGVyIHJhdGUgTVBQUyAoVFggb25seSkiKQ0KYGBgDQoNCiMjIFJlY2VpdmUNCg0KYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9DQp0eHJ4X2VweWMgPC0gZmlsdGVyKHR4cngsIHN5c3RlbT09IkFNRCBFUFlDIDc0NDNQIikNCmdncGxvdCh0eHJ4X2VweWMsIGFlcyh4PXBrdHNpemUsIGNvbG9yPXF1ZXVlcykpICsNCiAgZmFjZXRfZ3JpZChzeXN0ZW0gfiB3b3JrZXJzKSArDQogIGdlb21fbGluZShhZXMoeT1tYXhfcnhfbXBwcywgbGluZXR5cGU9IjBfcngiKSkgKw0KICBnZW9tX2xpbmUoYWVzKHk9bWF4X21wcHMsIGxpbmV0eXBlPSIxX3R4IikpICsgDQogIGdlb21fcG9pbnQoYWVzKHk9YXZnX3J4X21wcHMsIHNoYXBlPSJhdmciKSwgYWxwaGE9MC41KSArDQogIGdlb21fcG9pbnQoYWVzKHk9bWF4X3J4X21wcHMsIHNoYXBlPSJtYXgiKSwgYWxwaGE9MC41KSArDQogIGdlb21fcG9pbnQoYWVzKHk9bWluX3J4X21wcHMsIHNoYXBlPSJtaW4iKSwgYWxwaGE9MC41KSArDQogIGdlb21fbGluZShhZXMoeT1MaW5lcmF0ZSgxMDAsIHBrdHNpemUpLzFlNiwgbGluZXR5cGU9IjJfbGluZXJhdGUiKSwgY29sb3I9J2dyZXknKSArDQogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoTkEsIG1heCh0eHJ4X2VweWMkbWF4X21wcHMpKSkgKw0KICBnZ3RpdGxlKCJNdWx0aSBjb3JlIHBlcmZvcm1hbmNlIGJ5IG51bWJlciBvZiBxdWV1ZXMgcGVyIHdvcmtlciBhbmQgcGFja2V0IHNpemUiLA0KICAgICAgICAgIHN1YnRpdGxlPSJhcHBzLm1lbGxhbm94LmNvbm5lY3R4OiBSWCByYXRlIG9mIGNvbWJpbmVkIHJlY2VpdmUgcXVldWVzIGluIE1QUFMiKQ0KDQpgYGANCg0KDQojIFJYIERyb3BzDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQ0KZ2dwbG90KHR4cngsIGFlcyh4PXBrdHNpemUsIGNvbG9yPXF1ZXVlcykpICsNCiAgZmFjZXRfZ3JpZChzeXN0ZW0gfiB3b3JrZXJzKSArDQogIGdlb21fbGluZShhZXMoeT1tYXhfbXBwcy1tYXhfcnhfbXBwcywgbGluZXR5cGU9IjBfZHJvcCIpKSArDQogIGdlb21fbGluZShhZXMoeT1tYXhfbXBwcywgbGluZXR5cGU9IjFfdHgiKSkgKyANCiAgZ2VvbV9wb2ludChhZXMoeT1hdmdfbXBwcy1hdmdfcnhfbXBwcywgc2hhcGU9ImF2ZyIpLCBhbHBoYT0wLjUpICsNCiAgZ2VvbV9wb2ludChhZXMoeT1tYXhfbXBwcy1tYXhfcnhfbXBwcywgc2hhcGU9Im1heCIpLCBhbHBoYT0wLjUpICsNCiAgZ2VvbV9wb2ludChhZXMoeT1taW5fbXBwcy1taW5fcnhfbXBwcywgc2hhcGU9Im1pbiIpLCBhbHBoYT0wLjUpICsNCiAgZ2VvbV9saW5lKGFlcyh5PUxpbmVyYXRlKDEwMCwgcGt0c2l6ZSkvMWU2LCBsaW5ldHlwZT0iMl9saW5lcmF0ZSIpLCBjb2xvcj0nZ3JleScpICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YyhOQSwgbWF4KHR4cngkbWF4X21wcHMpKSkgKw0KICBnZ3RpdGxlKCJNdWx0aSBjb3JlIHBlcmZvcm1hbmNlIGJ5IG51bWJlciBvZiBxdWV1ZXMgcGVyIHdvcmtlciBhbmQgcGFja2V0IHNpemUiLA0KICAgICAgICAgIHN1YnRpdGxlPSJhcHBzLm1lbGxhbm94LmNvbm5lY3R4OiBMb3NzIHJhdGUgb2YgY29tYmluZWQgcmVjZWl2ZSBxdWV1ZXMgaW4gTVBQUyIpDQpgYGANCg0KIyBIZWxwDQoNClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIA0KDQpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouDQoNCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4NCg0KV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuDQoNClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4NCg==