aboutsummaryrefslogtreecommitdiffstats
path: root/vhost-test/mg.lua
blob: 21c4afa533e8e9e65635b006ffca8e0740b2efb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
local mg      = require "moongen"
local memory  = require "memory"
local device  = require "device"
local stats   = require "stats"
local hist    = require "histogram"
local timer   = require "timer"
local barrier = require "barrier"
local table   = require "table"
local math    = require "math"

function configure(parser)
	parser:description("Packet loss graph.")
	parser:option("--txport", "Device to transmit/receive from."):convert(tonumber)
	parser:option("--rxport", "Device to transmit/receive from."):convert(tonumber)
	parser:option("--dst", "Frame's destination hardware address")
	parser:option("--duration", "Device to transmit/receive from."):default(20):convert(tonumber)
	parser:option("--frameSize", "Ethernet frame size."):default(64):convert(tonumber)
	parser:option("--maxRateInterval", "Max % rate interval between measures"):default(5):convert(tonumber)
	parser:option("--minRateInterval", "Min % rate interval between measures"):default(0.1):convert(tonumber)
	parser:option("--targetDropRateRatio", "Target drop rate ratio between measures"):default(2):convert(tonumber)
	parser:option("--out", "Output file"):default("vhost_mg_"..os.date("%F_%H-%M")..".txt")
end

function getHeaderString(file)
	return string.format("%10s\t%10s\t%12s\t%12s\t%12s\t%12s\t%12s\t%12s\t%12s\t%12s", 
	"size(B)", "Time(s)", "Rate(Mbps)", "Tx(pkt)", "Rx(pkt)", "DropRate(%)", "Tx(Mpps)", "Rx(Mpps)", "Tx(Gbps)", "Rx(Gbps)")
end

function getResultString(result, file)
	return string.format("%10d\t%10d\t%12f\t%12d\t%12d\t%12f\t%12f\t%12f\t%12f\t%12f", 
	result.frameSize, result.duration, result.rate, result.tx, result.rx, result.dropRate, result.txMpps, result.rxMpps, result.txRate, result.rxRate)
end

function runMeasure(txDev, rxDev, frameSize, duration, rate)
	local bar = barrier.new(2)
	local result = {}
	txDev:getTxQueue(0):setRate(rate * frameSize / (frameSize+20))
	local stask = mg.startTask("loadSlave", txDev:getTxQueue(0), frameSize, duration, bar)
	local rtask = mg.startTask("counterSlave", rxDev:getRxQueue(0), frameSize, duration, bar)
	result.frameSize = frameSize
	result.duration = duration
	result.tx = stask:wait()
	result.rx = rtask:wait()
	result.rate = rate
	result.dropRate = (result.tx - result.rx)/result.tx * 100
	result.txMpps = (result.tx) / duration / 1000000
	result.rxMpps = (result.rx) / duration / 1000000
	result.txRate = (result.tx * frameSize * 8) / duration / 1000000000
	result.rxRate = (result.rx * frameSize * 8) / duration / 1000000000
	
	print(getResultString(result))
	return result
end

function master(args)
	local txDev = device.config({port = args.txport, rxQueues = 1, txQueues = 1})
	local rxDev = device.config({port = args.rxport, rxQueues = 1, txQueues = 1})
	local frameSize = args.frameSize
	local duration = args.duration
	device.waitForLinks()
	
	local maxLinkRate = txDev:getLinkStatus().speed
	local results = {}
	
	-- Warming up
	print ("Output file is: "..args.out)
	print ("Start Warm-Up")
	runMeasure(txDev, rxDev, frameSize, 1, maxLinkRate)
	runMeasure(txDev, rxDev, frameSize, 1, 1000)
	print ("Stop Warm-Up")
	
	local sortFunction = function(a,b)
		return a.rate < b.rate
	end
	
	local file = io.open(args.out, "w")
	io.output(file)
	io.write(getHeaderString().."\n")
	io.close(file)
	
	print ("Start Measures")
	print(getHeaderString())
	table.insert(results, runMeasure(txDev, rxDev, frameSize, duration, maxLinkRate))
	table.insert(results, runMeasure(txDev, rxDev, frameSize, duration, maxLinkRate/2))
	table.insert(results, runMeasure(txDev, rxDev, frameSize, duration, 100))
	table.sort(results, sortFunction)
	
	while mg.running() do
		local chosen = nil
		local chosenMeaning = 0
		for i, r1 in ipairs(results) do
			local r2 = results[i+1]
			if r2 == nil then
				break
			end
			local rateDiff = r2.rate - r1.rate
			local dropRateRatio = (r2.dropRate + 0.00000001)/(r1.dropRate + 0.00000001)
			if r2.dropRate < r1.dropRate then  
				dropRateRatio = (r1.dropRate+ 0.00000001)/(r2.dropRate + 0.00000001)
			end
			-- Meaning rates next measures
			-- The idea is to compute interesting results first and get more picky later.
			local meaning = rateDiff / ((args.maxRateInterval * maxLinkRate) / 100 ) + math.log(dropRateRatio / args.targetDropRateRatio)/math.log(2)
			if rateDiff > (args.maxRateInterval * maxLinkRate) / 100
				or (dropRateRatio > args.targetDropRateRatio and (rateDiff) > (args.minRateInterval * maxLinkRate) / 100) then
					if meaning > chosenMeaning then
						chosenMeaning = meaning
						chosen = i
					end
			end
		end
		if (chosen == nil) then
			break
		end
		local nextRate = (results[chosen+1].rate + results[chosen].rate)/2
		table.insert(results, runMeasure(txDev, rxDev, frameSize, duration, nextRate))
		table.sort(results, sortFunction)
		
		local file = io.open(args.out, "w")
		io.output(file)
		io.write(getHeaderString().."\n")
		for i, r1 in ipairs(results) do
			io.write(getResultString(r1).."\n")
		end
		io.close(file)
	end
	
	
end

function loadSlave(queue, frameSize, duration, bar)
    bar:wait()

	local mem = memory.createMemPool(function(buf)
		buf:getEthernetPacket():fill{
			ethSrc = queue,
			ethDst = ETH_DST,
			ethType = 0x1234
		}
	end)

    local bufs = mem:bufArray()
    local timer = timer:new(duration)
	local total = 0;
	while timer:running() do
		bufs:alloc(frameSize)
		total = total + queue:send(bufs)
	end
	return total
end

function counterSlave(queue, frameSize, duration, bar)
    local bufs = memory.bufArray()
	local total = 0;
    bar:wait()

    local timer = timer:new(duration + 1)
    while timer:running() do
		total = total + queue:tryRecv(bufs, 1000)
		bufs:freeAll()
    end
    return total
end