Skip to content

Commit

Permalink
Optimized compiler/circuits/Wire memory usage.
Browse files Browse the repository at this point in the history
Cleaned up circuits compiler API.
  • Loading branch information
markkurossi committed Aug 30, 2023
1 parent b4486b0 commit 2829b3a
Show file tree
Hide file tree
Showing 17 changed files with 365 additions and 310 deletions.
Binary file modified apps/garbled/default.pgo
Binary file not shown.
25 changes: 25 additions & 0 deletions benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,31 @@ Max permanent wires: 53913890, cached circuits: 25
#gates=830166294 (XOR=533177896 XNOR=28813441 AND=267575026 OR=496562 INV=103369 xor=561991337 !xor=268174957 levels=10548 width=1796) #w=853882864
```

Optimized `compiler/circuits/Wire`:

```
┌──────────────┬────────────────┬─────────┬────────┐
│ Op │ Time │ % │ Xfer │
├──────────────┼────────────────┼─────────┼────────┤
│ Compile │ 1.870993069s │ 2.71% │ │
│ Init │ 2.331431ms │ 0.00% │ 0B │
│ OT Init │ 10.949µs │ 0.00% │ 16kB │
│ Peer Inputs │ 44.089085ms │ 0.06% │ 57kB │
│ Stream │ 1m7.0813688s │ 97.22% │ 15GB │
│ ├╴InstrInit │ 2.421297578s │ 3.61% │ │
│ ├╴CircComp │ 17.09415ms │ 0.03% │ │
│ ├╴StreamInit │ 2.155089182s │ 3.21% │ │
│ ╰╴Garble │ 1m1.550598148s │ 91.76% │ │
│ Result │ 432.27µs │ 0.00% │ 8kB │
│ Total │ 1m8.999225604s │ │ 15GB │
│ ├╴Sent │ │ 100.00% │ 15GB │
│ ├╴Rcvd │ │ 0.00% │ 45kB │
│ ╰╴Flcd │ │ │ 231284 │
└──────────────┴────────────────┴─────────┴────────┘
Max permanent wires: 53913890, cached circuits: 25
#gates=830166294 (XOR=533177896 XNOR=28813441 AND=267575026 OR=496562 INV=103369 xor=561991337 !xor=268174957 levels=10548 width=1796) #w=853882864
```

Theoretical minimum single-threaded garbling time:

```
Expand Down
65 changes: 54 additions & 11 deletions compiler/circuits/allocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@ package circuits

import (
"fmt"
"unsafe"

"github.com/markkurossi/mpc/circuit"
"github.com/markkurossi/mpc/types"
)

var (
sizeofWire = uint64(unsafe.Sizeof(Wire{}))
sizeofGate = uint64(unsafe.Sizeof(Gate{}))
)

// Allocator implements circuit wire and gate allocation.
type Allocator struct {
block []Wire
ofs int
numWire uint64
numWires uint64
numGates uint64
}

// NewAllocator creates a new circuit allocator.
Expand All @@ -36,23 +42,60 @@ func (alloc *Allocator) Wire() *Wire {
// Wires allocate an array of Wires.
func (alloc *Allocator) Wires(bits types.Size) []*Wire {
alloc.numWires += uint64(bits)

wires := make([]Wire, bits)
result := make([]*Wire, bits)
for i := 0; i < int(bits); i++ {
if alloc.ofs == 0 {
alloc.ofs = 8192
alloc.block = make([]Wire, alloc.ofs)
}
alloc.ofs--
w := &alloc.block[alloc.ofs]

w := &wires[i]
w.id = UnassignedID
result[i] = w
}
return result
}

// BinaryGate creates a new binary gate.
func (alloc *Allocator) BinaryGate(op circuit.Operation, a, b, o *Wire) *Gate {
alloc.numGates++
gate := &Gate{
Op: op,
A: a,
B: b,
O: o,
}
a.AddOutput(gate)
b.AddOutput(gate)
o.SetInput(gate)

return gate
}

// INVGate creates a new INV gate.
func (alloc *Allocator) INVGate(i, o *Wire) *Gate {
alloc.numGates++
gate := &Gate{
Op: circuit.INV,
A: i,
O: o,
}
i.AddOutput(gate)
o.SetInput(gate)

return gate
}

// Debug print debugging information about the circuit allocator.
func (alloc *Allocator) Debug() {
fmt.Printf("circuits.Allocator: #wire=%v, #wires=%v\n",
alloc.numWire, alloc.numWires)
wireSize := circuit.FileSize(alloc.numWire * sizeofWire)
wiresSize := circuit.FileSize(alloc.numWires * sizeofWire)
gatesSize := circuit.FileSize(alloc.numGates * sizeofGate)

total := float64(wireSize + wiresSize + gatesSize)

fmt.Println("circuits.Allocator:")
fmt.Printf(" wire : %9v %5s %5.2f%%\n",
alloc.numWire, wireSize, float64(wireSize)/total*100.0)
fmt.Printf(" wires: %9v %5s %5.2f%%\n",
alloc.numWires, wiresSize, float64(wiresSize)/total*100.0)
fmt.Printf(" gates: %9v %5s %5.2f%%\n",
alloc.numGates, gatesSize, float64(gatesSize)/total*100.0)
}
40 changes: 20 additions & 20 deletions compiler/circuits/circ_adder.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,46 @@ import (
)

// NewHalfAdder creates a half adder circuit.
func NewHalfAdder(compiler *Compiler, a, b, s, c *Wire) {
func NewHalfAdder(cc *Compiler, a, b, s, c *Wire) {
// S = XOR(A, B)
compiler.AddGate(NewBinary(circuit.XOR, a, b, s))
cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, a, b, s))

if c != nil {
// C = AND(A, B)
compiler.AddGate(NewBinary(circuit.AND, a, b, c))
cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, a, b, c))
}
}

// NewFullAdder creates a full adder circuit
func NewFullAdder(compiler *Compiler, a, b, cin, s, cout *Wire) {
w1 := compiler.Calloc.Wire()
w2 := compiler.Calloc.Wire()
w3 := compiler.Calloc.Wire()
func NewFullAdder(cc *Compiler, a, b, cin, s, cout *Wire) {
w1 := cc.Calloc.Wire()
w2 := cc.Calloc.Wire()
w3 := cc.Calloc.Wire()

// s = a XOR b XOR cin
// cout = cin XOR ((a XOR cin) AND (b XOR cin)).

// w1 = XOR(b, cin)
compiler.AddGate(NewBinary(circuit.XOR, b, cin, w1))
cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, b, cin, w1))

// s = XOR(a, w1)
compiler.AddGate(NewBinary(circuit.XOR, a, w1, s))
cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, a, w1, s))

if cout != nil {
// w2 = XOR(a, cin)
compiler.AddGate(NewBinary(circuit.XOR, a, cin, w2))
cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, a, cin, w2))

// w3 = AND(w1, w2)
compiler.AddGate(NewBinary(circuit.AND, w1, w2, w3))
cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, w1, w2, w3))

// cout = XOR(cin, w3)
compiler.AddGate(NewBinary(circuit.XOR, cin, w3, cout))
cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, cin, w3, cout))
}
}

// NewAdder creates a new adder circuit implementing z=x+y.
func NewAdder(compiler *Compiler, x, y, z []*Wire) error {
x, y = compiler.ZeroPad(x, y)
func NewAdder(cc *Compiler, x, y, z []*Wire) error {
x, y = cc.ZeroPad(x, y)
if len(x) > len(z) {
x = x[0:len(z)]
y = y[0:len(z)]
Expand All @@ -63,10 +63,10 @@ func NewAdder(compiler *Compiler, x, y, z []*Wire) error {
if len(z) > 1 {
cin = z[1]
}
NewHalfAdder(compiler, x[0], y[0], z[0], cin)
NewHalfAdder(cc, x[0], y[0], z[0], cin)
} else {
cin := compiler.Calloc.Wire()
NewHalfAdder(compiler, x[0], y[0], z[0], cin)
cin := cc.Calloc.Wire()
NewHalfAdder(cc, x[0], y[0], z[0], cin)

for i := 1; i < len(x); i++ {
var cout *Wire
Expand All @@ -78,18 +78,18 @@ func NewAdder(compiler *Compiler, x, y, z []*Wire) error {
cout = z[len(x)]
}
} else {
cout = compiler.Calloc.Wire()
cout = cc.Calloc.Wire()
}

NewFullAdder(compiler, x[i], y[i], cin, z[i], cout)
NewFullAdder(cc, x[i], y[i], cin, z[i], cout)

cin = cout
}
}

// Set all leftover bits to zero.
for i := len(x) + 1; i < len(z); i++ {
z[i] = compiler.ZeroWire()
z[i] = cc.ZeroWire()
}

return nil
Expand Down
28 changes: 14 additions & 14 deletions compiler/circuits/circ_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,55 @@ import (
)

// NewBinaryAND creates a new binary AND circuit implementing r=x&y
func NewBinaryAND(compiler *Compiler, x, y, r []*Wire) error {
x, y = compiler.ZeroPad(x, y)
func NewBinaryAND(cc *Compiler, x, y, r []*Wire) error {
x, y = cc.ZeroPad(x, y)
if len(r) < len(x) {
x = x[0:len(r)]
y = y[0:len(r)]
}
for i := 0; i < len(x); i++ {
compiler.AddGate(NewBinary(circuit.AND, x[i], y[i], r[i]))
cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, x[i], y[i], r[i]))
}
return nil
}

// NewBinaryClear creates a new binary clear circuit implementing r=x&^y.
func NewBinaryClear(compiler *Compiler, x, y, r []*Wire) error {
x, y = compiler.ZeroPad(x, y)
func NewBinaryClear(cc *Compiler, x, y, r []*Wire) error {
x, y = cc.ZeroPad(x, y)
if len(r) < len(x) {
x = x[0:len(r)]
y = y[0:len(r)]
}
for i := 0; i < len(x); i++ {
w := compiler.Calloc.Wire()
compiler.INV(y[i], w)
compiler.AddGate(NewBinary(circuit.AND, x[i], w, r[i]))
w := cc.Calloc.Wire()
cc.INV(y[i], w)
cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, x[i], w, r[i]))
}
return nil
}

// NewBinaryOR creates a new binary OR circuit implementing r=x|y.
func NewBinaryOR(compiler *Compiler, x, y, r []*Wire) error {
x, y = compiler.ZeroPad(x, y)
func NewBinaryOR(cc *Compiler, x, y, r []*Wire) error {
x, y = cc.ZeroPad(x, y)
if len(r) < len(x) {
x = x[0:len(r)]
y = y[0:len(r)]
}
for i := 0; i < len(x); i++ {
compiler.AddGate(NewBinary(circuit.OR, x[i], y[i], r[i]))
cc.AddGate(cc.Calloc.BinaryGate(circuit.OR, x[i], y[i], r[i]))
}
return nil
}

// NewBinaryXOR creates a new binary XOR circuit implementing r=x^y.
func NewBinaryXOR(compiler *Compiler, x, y, r []*Wire) error {
x, y = compiler.ZeroPad(x, y)
func NewBinaryXOR(cc *Compiler, x, y, r []*Wire) error {
x, y = cc.ZeroPad(x, y)
if len(r) < len(x) {
x = x[0:len(r)]
y = y[0:len(r)]
}
for i := 0; i < len(x); i++ {
compiler.AddGate(NewBinary(circuit.XOR, x[i], y[i], r[i]))
cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, x[i], y[i], r[i]))
}
return nil
}
Loading

0 comments on commit 2829b3a

Please sign in to comment.