-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Imported from Igalia/lwaftr_starfruit, up to date with commit a87632f
- Loading branch information
Showing
343 changed files
with
14,394 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
-- Source -> NIC1 -> NIC2 -> Sink | ||
|
||
local Intel82599 = require("apps.intel.intel_app").Intel82599 | ||
local PcapReader = require("apps.pcap.pcap").PcapReader | ||
local basic_apps = require("apps.basic.basic_apps") | ||
local counter = require("core.counter") | ||
local ffi = require("ffi") | ||
local lib = require("core.lib") | ||
local lwaftr = require("apps.lwaftr.lwaftr") | ||
|
||
local C = ffi.C | ||
|
||
local paths = { | ||
["nicv4-in"] = "nicv4.input.rx", | ||
["nicv6-in"] = "nicv6.input.rx", | ||
["nicv4-out"] = "nicv4.output.tx", | ||
["nicv6-out"] = "nicv6.output.tx", | ||
["lwaftrv4-out"] = "lwaftr.output.v4", | ||
["lwaftrv6-out"] = "lwaftr.output.v6", | ||
["lwaftrv4-in"] = "lwaftr.input.v4", | ||
["lwaftrv6-in"] = "lwaftr.input.v6", | ||
} | ||
|
||
local function split (str, sep) | ||
local t = {} | ||
local regex = ("([^%s]+)"):format(sep) | ||
for each in str:gmatch(regex) do | ||
table.insert(t, each) | ||
end | ||
return t | ||
end | ||
|
||
local function bench(engine, params) | ||
local function format (str, t) | ||
for key, _ in str:gmatch("{([a-zA-Z_]+)}") do | ||
str = str:gsub("{"..key.."}", t[key]) | ||
end | ||
return str | ||
end | ||
local function report (name, breaths, bytes, packets, runtime) | ||
local values = { | ||
name = name, | ||
breath_in_nanosecond = ("%.2f"):format(runtime / breaths * 1e6), | ||
breaths = lib.comma_value(breaths), | ||
bytes = bytes, | ||
million_packets = ("%.1f"):format(packets / 1e6), | ||
packets_per_breath = ("%.2f"):format(packets / breaths), | ||
rate_gbps = ("%.2f"):format((bytes * 8 ) / 1e9 / runtime), | ||
rate_mpps = ("%.3f"):format(packets / runtime / 1e6), | ||
runtime = ("%.2f"):format(runtime), | ||
} | ||
print("\n"..format([[{name} processed {million_packets} million packets in {runtime} seconds ({bytes} bytes; {rate_gbps} Gbps) | ||
Made {breaths} breaths: {packets_per_breath} packets per breath; {breath_in_nanosecond} us per breath | ||
Rate(Mpps): {rate_mpps} | ||
]], values)) | ||
end | ||
local function report_bench(input, name, engine, finish, start) | ||
local breaths = tonumber(counter.read(engine.breaths)) | ||
local bytes = input.txbytes | ||
-- Don't bother to report on interfaces that were boring | ||
if bytes == 0 then return end | ||
local packets = input.txpackets | ||
local runtime = finish - start | ||
report(name, breaths, bytes, packets, runtime) | ||
end | ||
local function reports(names, engine, finish, start) | ||
for _, name in ipairs(names) do | ||
local parts = split(paths[name], ".") | ||
assert(#parts == 3, "Wrong path") | ||
local app_name, channel, direction = unpack(parts) | ||
local stats = link.stats(engine.app_table[app_name][channel][direction]) | ||
report_bench(stats, name, engine, finish, start) | ||
end | ||
end | ||
local start = C.get_monotonic_time() | ||
engine.main(params) | ||
local finish = C.get_monotonic_time() | ||
reports({"nicv4-in","nicv6-in"}, engine, finish, start) | ||
end | ||
|
||
local function usage () | ||
print([[ | ||
Usage: <conf_file> <pcap_file_v4> <pcap_file_v6> <pci_dev_v4> <pci_dev_v6> | ||
<conf_file>: Path to lwaftr configuration file. | ||
<pcap_file_v4>: Path to pcap file contain IPv4 packet/s to be sent. | ||
<pcap_file_v6>: Path to pcap file contain IPv6 packet/s to be sent. | ||
<pci_dev_v4>: PCI ID number of network card for IPv4. | ||
<pci_dev_v6>: PCI ID number of network card for IPv6. | ||
]]) | ||
os.exit() | ||
end | ||
|
||
local function testInternalLoopbackFromPcapFile (params) | ||
if #params ~= 5 then usage() end | ||
local conf_file, pcapv4_file, pcapv6_file, pcidev_v4, pcidev_v6 = unpack(params) | ||
|
||
engine.configure(config.new()) | ||
local c = config.new() | ||
config.app(c, 'lwaftr', lwaftr.LwAftr, conf_file) | ||
config.app(c, 'pcapv4', PcapReader, pcapv4_file) | ||
config.app(c, 'pcapv6', PcapReader, pcapv6_file) | ||
config.app(c, 'repeater_v4', basic_apps.Repeater) | ||
config.app(c, 'repeater_v6', basic_apps.Repeater) | ||
config.app(c, 'sink', basic_apps.Sink) | ||
|
||
-- Both nics are full-duplex | ||
config.app(c, 'nicv4', Intel82599, { | ||
pciaddr = pcidev_v4, | ||
macaddr = '22:22:22:22:22:22', | ||
}) | ||
|
||
config.app(c, 'nicv6', Intel82599, { | ||
pciaddr = pcidev_v6, | ||
macaddr = '44:44:44:44:44:44', | ||
}) | ||
|
||
config.link(c, 'pcapv4.output -> repeater_v4.input') | ||
config.link(c, 'repeater_v4.output -> lwaftr.v4') | ||
config.link(c, 'lwaftr.v4 -> nicv4.rx') | ||
config.link(c, 'nicv4.tx -> sink.in1') | ||
|
||
config.link(c, 'pcapv6.output -> repeater_v6.input') | ||
config.link(c, 'repeater_v6.output -> lwaftr.v6') | ||
config.link(c, 'lwaftr.v6 -> nicv6.rx') | ||
config.link(c, 'nicv6.tx -> sink.in1') | ||
|
||
engine.configure(c) | ||
|
||
bench(engine, {duration=5, report={showlinks=true}}) | ||
end | ||
|
||
testInternalLoopbackFromPcapFile(main.parameters) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
-- Optimized branchless binary search over sorted vectors -*- lua -*- | ||
-- | ||
-- An optimized implementation of branchless binary search, following | ||
-- the article by Paul Khuoung, "Binary search *eliminates* branch | ||
-- misprediction.": | ||
-- | ||
-- http://www.pvk.ca/Blog/2012/07/03/binary-search-star-eliminates-star-branch-mispredictions/ | ||
|
||
module(..., package.seeall) | ||
|
||
local debug = false | ||
|
||
local ffi = require("ffi") | ||
local bit = require("bit") | ||
local C = ffi.C | ||
|
||
local dasm = require("dasm") | ||
|
||
|.arch x64 | ||
|.actionlist actions | ||
|
||
-- Table keeping machine code alive to the GC. | ||
local anchor = {} | ||
|
||
-- Utility: assemble code and optionally dump disassembly. | ||
local function assemble (name, prototype, generator) | ||
local Dst = dasm.new(actions) | ||
generator(Dst) | ||
local mcode, size = Dst:build() | ||
table.insert(anchor, mcode) | ||
if debug then | ||
print("mcode dump: "..name) | ||
dasm.dump(mcode, size) | ||
end | ||
return ffi.cast(prototype, mcode) | ||
end | ||
|
||
function gen(count, entry_type) | ||
local function gen_binary_search(Dst) | ||
if count == 1 then | ||
| mov rax, rdi | ||
| ret | ||
return | ||
end | ||
|
||
local entry_byte_size = ffi.sizeof(entry_type) | ||
local size = 1 | ||
while size < count do size = size * 2 end | ||
|
||
-- Initially, the vector is in edi and the key we are looking for | ||
-- is in esi. Save the vector pointer in rdx. | ||
| mov rdx, rdi | ||
|
||
-- In the first bisection, make sure the rest of the bisections | ||
-- have a power-of-two size. | ||
do | ||
local next_size = size / 2 | ||
local mid = next_size - 1 | ||
local mid_offset = mid * entry_byte_size | ||
local hi_offset = (count - next_size) * entry_byte_size | ||
| cmp [rdi + mid_offset], esi | ||
| lea rax, [rdi + hi_offset] | ||
| cmovb rdi, rax | ||
size = size / 2 | ||
end | ||
|
||
-- In the rest, just burn down the halves. Wheeee! | ||
while size > 1 do | ||
local next_size = size / 2 | ||
local mid = next_size - 1 | ||
local mid_offset = mid * entry_byte_size | ||
local hi_offset = next_size * entry_byte_size | ||
| cmp [rdi + mid_offset], esi | ||
| lea rax, [rdi + hi_offset] | ||
| cmovb rdi, rax | ||
size = next_size | ||
end | ||
|
||
-- Now rdi points at the answer (if we have one). Done! | ||
| mov rax, rdi | ||
| ret | ||
end | ||
return assemble("binary_search_"..count, | ||
ffi.typeof("$*(*)($*, uint32_t)", entry_type, entry_type), | ||
gen_binary_search) | ||
end | ||
|
||
function selftest () | ||
print("selftest: binary_search") | ||
local test = ffi.new('uint32_t[15]', | ||
{ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5 }) | ||
local searchers = {} | ||
for i=1,10 do searchers[i] = gen(i, ffi.typeof('uint32_t')) end | ||
|
||
local function assert_search(size, key, expected) | ||
local res = searchers[size](test, key) - test | ||
if res ~= expected then | ||
error(('in search of size %d for key %d: expected %d, got %d'):format( | ||
size, key, expected, res)) | ||
end | ||
end | ||
|
||
for i=1,10 do | ||
assert_search(i, 0, 0) | ||
assert_search(i, 1, 0) | ||
assert_search(i, 6, i - 1) | ||
end | ||
|
||
for i=2,10 do | ||
assert_search(i, 2, 1) | ||
end | ||
|
||
for i=4,10 do | ||
assert_search(i, 3, 3) | ||
end | ||
|
||
for i=7,10 do | ||
assert_search(i, 4, 6) | ||
end | ||
|
||
print("selftest: ok") | ||
end |
Oops, something went wrong.