Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Go
name: Test kodr RLNC

on:
push:
Expand All @@ -11,12 +11,18 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: 1.16
go-version: 1.24

- name: Test
run: go test -v -cover ./...
- name: Run all tests
run: go test -v -cover -count=10 ./...

- name: Run Full RLNC example
run: pushd examples/full; go run main.go; popd

- name: Run Systematic RLNC example
run: pushd examples/systematic; go run main.go; popd
398 changes: 195 additions & 203 deletions README.md

Large diffs are not rendered by default.

57 changes: 28 additions & 29 deletions bench/full/decode_test.go → benches/full/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,11 @@ import (
"testing"
"time"

"github.com/itzmeanjan/kodr"
"github.com/itzmeanjan/kodr/full"
"github.com/itzmeanjan/kodr/kodr_internals"
)

func BenchmarkFullRLNCDecoder(t *testing.B) {
t.Run("256K", func(b *testing.B) {
b.Run("16 Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<18) })
b.Run("32 Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<18) })
b.Run("64 Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<18) })
b.Run("128 Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<18) })
b.Run("256 Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<18) })
})

t.Run("512K", func(b *testing.B) {
b.Run("16 Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<19) })
b.Run("32 Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<19) })
b.Run("64 Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<19) })
b.Run("128 Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<19) })
b.Run("256 Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<19) })
})

t.Run("1M", func(b *testing.B) {
b.Run("16 Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<20) })
b.Run("32 Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<20) })
Expand All @@ -41,44 +25,59 @@ func BenchmarkFullRLNCDecoder(t *testing.B) {
b.Run("128 Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<21) })
b.Run("256 Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<21) })
})

t.Run("16M", func(b *testing.B) {
b.Run("16 Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<24) })
b.Run("32 Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<24) })
b.Run("64 Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<24) })
b.Run("128 Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<24) })
b.Run("256 Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<24) })
})

t.Run("32M", func(b *testing.B) {
b.Run("16 Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<25) })
b.Run("32 Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<25) })
b.Run("64 Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<25) })
b.Run("128 Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<25) })
b.Run("256 Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<25) })
})
}

func decode(t *testing.B, pieceCount uint, total uint) {
rand.Seed(time.Now().UnixNano())
data := generateRandomData(total)

data := generateData(total)
enc, err := full.NewFullRLNCEncoderWithPieceCount(data, pieceCount)
if err != nil {
t.Fatalf("Error: %s\n", err.Error())
}

pieces := make([]*kodr.CodedPiece, 0, 2*pieceCount)
for i := 0; i < int(2*pieceCount); i++ {
pieces := make([]*kodr_internals.CodedPiece, 0, 2*pieceCount)
for range 2 * pieceCount {
pieces = append(pieces, enc.CodedPiece())
}

t.ResetTimer()

totalDuration := 0 * time.Second
for i := 0; i < t.N; i++ {
totalDuration += decode_(t, pieceCount, pieces)
for t.Loop() {
totalDuration += decode_internal(t, pieceCount, pieces)
}

t.ReportMetric(0, "ns/op")
t.ReportMetric(float64(totalDuration.Seconds())/float64(t.N), "second/decode")
}

func decode_(t *testing.B, pieceCount uint, pieces []*kodr.CodedPiece) time.Duration {
func decode_internal(t *testing.B, pieceCount uint, pieces []*kodr_internals.CodedPiece) time.Duration {
dec := full.NewFullRLNCDecoder(pieceCount)

// randomly shuffle piece ordering
rand.Shuffle(int(2*pieceCount), func(i, j int) {
// Random shuffle piece ordering
rand.Shuffle(len(pieces), func(i, j int) {
pieces[i], pieces[j] = pieces[j], pieces[i]
})

totalDuration := 0 * time.Second
for j := 0; j < int(2*pieceCount); j++ {
if j+1 >= int(pieceCount) && dec.IsDecoded() {
for j := range 2 * pieceCount {
if j+1 >= pieceCount && dec.IsDecoded() {
break
}

Expand All @@ -88,7 +87,7 @@ func decode_(t *testing.B, pieceCount uint, pieces []*kodr.CodedPiece) time.Dura
}

if !dec.IsDecoded() {
t.Fatal("expected pieces to be decoded")
t.Fatal("expected pieces to be already decoded")
}

return totalDuration
Expand Down
20 changes: 7 additions & 13 deletions bench/full/encode_test.go → benches/full/encoder_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package full_test

import (
"math/rand"
"crypto/rand"
"testing"
"time"

"github.com/itzmeanjan/kodr/full"
)
Expand Down Expand Up @@ -34,32 +33,27 @@ func BenchmarkFullRLNCEncoder(t *testing.B) {
})
}

// generate random data of N-bytes
func generateData(n uint) []byte {
// Generate random data of N-bytes
func generateRandomData(n uint) []byte {
data := make([]byte, n)
// can safely ignore error
rand.Read(data)

return data
}

func encode(t *testing.B, pieceCount uint, total uint) {
// non-reproducible random number sequence
rand.Seed(time.Now().UnixNano())
data := generateRandomData(total)

data := generateData(total)
enc, err := full.NewFullRLNCEncoderWithPieceCount(data, pieceCount)
if err != nil {
t.Fatalf("Error: %s\n", err.Error())
}

t.ReportAllocs()
// because pieceSize = total / pieceCount
// so each coded piece = pieceCount + pieceSize bytes
t.SetBytes(int64(total) + int64(pieceCount+total/pieceCount))
t.SetBytes(int64(total+enc.Padding()) + int64(enc.CodedPieceLen()))
t.ResetTimer()

// keep generating encoded pieces on-the-fly
for i := 0; i < t.N; i++ {
for t.Loop() {
enc.CodedPiece()
}
}
25 changes: 8 additions & 17 deletions bench/full/recode_test.go → benches/full/recoder_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package full_test

import (
"math/rand"
"testing"
"time"

"github.com/itzmeanjan/kodr"
"github.com/itzmeanjan/kodr/full"
"github.com/itzmeanjan/kodr/kodr_internals"
)

func BenchmarkFullRLNCRecoder(t *testing.B) {
Expand Down Expand Up @@ -36,33 +34,26 @@ func BenchmarkFullRLNCRecoder(t *testing.B) {
}

func recode(t *testing.B, pieceCount uint, total uint) {
// non-reproducible sequence
rand.Seed(time.Now().UnixNano())

// -- encode
data := generateData(total)
// Encode
data := generateRandomData(total)
enc, err := full.NewFullRLNCEncoderWithPieceCount(data, pieceCount)
if err != nil {
t.Fatalf("Error: %s\n", err.Error())
}

pieces := make([]*kodr.CodedPiece, 0, pieceCount)
for i := 0; i < int(pieceCount); i++ {
pieces := make([]*kodr_internals.CodedPiece, 0, pieceCount)
for range pieceCount {
pieces = append(pieces, enc.CodedPiece())
}
// -- encoding ends

// -- recode
// Recode
rec := full.NewFullRLNCRecoder(pieces)

t.ReportAllocs()
t.SetBytes(int64((pieceCount+total/pieceCount)*pieceCount) + int64(pieceCount+total/pieceCount))
t.ResetTimer()

for i := 0; i < t.N; i++ {
if _, err := rec.CodedPiece(); err != nil {
t.Fatalf("Error: %s\n", err.Error())
}
for t.Loop() {
rec.CodedPiece()
}
// -- recoding ends
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@ import (
"testing"
"time"

"github.com/itzmeanjan/kodr"
"github.com/itzmeanjan/kodr/full"
"github.com/itzmeanjan/kodr/kodr_internals"
"github.com/itzmeanjan/kodr/systematic"
)

func BenchmarkFullRLNCDecoder(t *testing.B) {
func BenchmarkSystematicRLNCDecoder(t *testing.B) {
t.Run("1M", func(b *testing.B) {
b.Run("16Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<20) })
b.Run("32Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<20) })
b.Run("64Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<20) })
b.Run("128Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<20) })
b.Run("256Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<20) })
b.Run("512Pieces", func(b *testing.B) { decode(b, 1<<9, 1<<20) })
})

t.Run("2M", func(b *testing.B) {
Expand All @@ -26,46 +24,60 @@ func BenchmarkFullRLNCDecoder(t *testing.B) {
b.Run("64Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<21) })
b.Run("128Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<21) })
b.Run("256Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<21) })
b.Run("512Pieces", func(b *testing.B) { decode(b, 1<<9, 1<<21) })
})

t.Run("16M", func(b *testing.B) {
b.Run("16 Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<24) })
b.Run("32 Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<24) })
b.Run("64 Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<24) })
b.Run("128 Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<24) })
b.Run("256 Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<24) })
})

t.Run("32M", func(b *testing.B) {
b.Run("16 Pieces", func(b *testing.B) { decode(b, 1<<4, 1<<25) })
b.Run("32 Pieces", func(b *testing.B) { decode(b, 1<<5, 1<<25) })
b.Run("64 Pieces", func(b *testing.B) { decode(b, 1<<6, 1<<25) })
b.Run("128 Pieces", func(b *testing.B) { decode(b, 1<<7, 1<<25) })
b.Run("256 Pieces", func(b *testing.B) { decode(b, 1<<8, 1<<25) })
})
}

func decode(t *testing.B, pieceCount uint, total uint) {
rand.Seed(time.Now().UnixNano())
data := generateRandomData(total)

data := generateData(total)
enc, err := systematic.NewSystematicRLNCEncoderWithPieceCount(data, pieceCount)
if err != nil {
t.Fatalf("Error: %s\n", err.Error())
}

pieces := make([]*kodr.CodedPiece, 0, 2*pieceCount)
for i := 0; i < int(2*pieceCount); i++ {
pieces := make([]*kodr_internals.CodedPiece, 0, 2*pieceCount)
for range 2 * pieceCount {
pieces = append(pieces, enc.CodedPiece())
}

t.ResetTimer()

totalDuration := 0 * time.Second
for i := 0; i < t.N; i++ {
totalDuration += decode_(t, pieceCount, pieces)
for t.Loop() {
totalDuration += decode_internal(t, pieceCount, pieces)
}

t.ReportMetric(0, "ns/op")
t.ReportMetric(float64(totalDuration.Seconds())/float64(t.N), "second/decode")
t.ReportMetric(float64(totalDuration.Seconds())/float64(t.N), "seconds/decode")
}

func decode_(t *testing.B, pieceCount uint, pieces []*kodr.CodedPiece) time.Duration {
dec := full.NewFullRLNCDecoder(pieceCount)
func decode_internal(t *testing.B, pieceCount uint, pieces []*kodr_internals.CodedPiece) time.Duration {
dec := systematic.NewSystematicRLNCDecoder(pieceCount)

// randomly shuffle piece ordering
rand.Shuffle(int(2*pieceCount), func(i, j int) {
// Random shuffle piece ordering
rand.Shuffle(len(pieces), func(i, j int) {
pieces[i], pieces[j] = pieces[j], pieces[i]
})

totalDuration := 0 * time.Second
for j := 0; j < int(2*pieceCount); j++ {
if j+1 >= int(pieceCount) && dec.IsDecoded() {
for j := range 2 * pieceCount {
if j+1 >= pieceCount && dec.IsDecoded() {
break
}

Expand All @@ -75,7 +87,7 @@ func decode_(t *testing.B, pieceCount uint, pieces []*kodr.CodedPiece) time.Dura
}

if !dec.IsDecoded() {
t.Fatal("expected pieces to be decoded")
t.Fatal("expected pieces to be already decoded")
}

return totalDuration
Expand Down
Loading