Re-worked memory management of blocks loaded from redis client to avoid

unnecessary copying them around.
This commit is contained in:
Sascha L. Teichmann 2017-03-03 23:00:29 +01:00
parent 4a3fa1f568
commit 91959685f5
4 changed files with 51 additions and 48 deletions

View File

@ -105,14 +105,21 @@ func main() {
renderer := common.NewRenderer(width, height, transparent) renderer := common.NewRenderer(width, height, transparent)
renderer.SetPos(q1x, q1z) renderer.SetPos(q1x, q1z)
yOrder := common.NewYOrder(renderer, 512)
renderFn := func(block *common.Block) error {
return renderer.RenderBlock(block, colors)
}
yOrder := common.NewYOrder(renderFn, 512)
numBlocks := 0 numBlocks := 0
drawBlock := func(block *common.Block) { drawBlock := func(block *common.Block) *common.Block {
if err := yOrder.RenderBlock(block, colors); err != nil { block, err := yOrder.RenderBlock(block)
if err != nil {
log.Printf("WARN: rendering block failed: %s\n", err) log.Printf("WARN: rendering block failed: %s\n", err)
} }
numBlocks++ numBlocks++
return block
} }
c1 := common.Coord{X: q1x, Z: q1z} c1 := common.Coord{X: q1x, Z: q1z}
@ -126,7 +133,7 @@ func main() {
if _, err = client.QueryCuboid(cuboid, drawBlock); err != nil { if _, err = client.QueryCuboid(cuboid, drawBlock); err != nil {
log.Fatalf("query failed: %s", err) log.Fatalf("query failed: %s", err)
} }
if err = yOrder.Drain(colors); err != nil { if err = yOrder.Drain(); err != nil {
log.Printf("WARN: rendering block failed: %s\n", err) log.Printf("WARN: rendering block failed: %s\n", err)
} }
if renderer.IsFilled() { if renderer.IsFilled() {

View File

@ -97,10 +97,12 @@ func (btc *BaseTileCreator) renderBlock(block *Block) error {
} }
// blockLoaded is a callback for RedisClient.QueryCuboid. // blockLoaded is a callback for RedisClient.QueryCuboid.
func (btc *BaseTileCreator) blockLoaded(block *Block) { func (btc *BaseTileCreator) blockLoaded(block *Block) *Block {
if err := btc.yOrder.RenderBlock(block); err != nil { block, err := btc.yOrder.RenderBlock(block)
if err != nil {
log.Printf("WARN: rendering block failed: %s\n", err) log.Printf("WARN: rendering block failed: %s\n", err)
} }
return block
} }
func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) { func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) {

View File

@ -11,13 +11,13 @@ import (
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
"sync"
"unicode" "unicode"
) )
type RedisClient struct { type RedisClient struct {
conn net.Conn conn net.Conn
reader *bufio.Reader reader *bufio.Reader
arena []byte
scratch [80]byte scratch [80]byte
} }
@ -89,6 +89,16 @@ func parseSize(line []byte) (int, error) {
return int(v), err return int(v), err
} }
func (client *RedisClient) alloc(size int) []byte {
a := client.arena
if len(a) < size {
a = make([]byte, Max(size, 16*1024))
}
x := a[:size:size]
client.arena = a[size:]
return x
}
func (client *RedisClient) readBulkString(data *[]byte) (size int, err error) { func (client *RedisClient) readBulkString(data *[]byte) (size int, err error) {
var line []byte var line []byte
if line, err = client.reader.ReadBytes('\n'); err != nil { if line, err = client.reader.ReadBytes('\n'); err != nil {
@ -101,7 +111,9 @@ func (client *RedisClient) readBulkString(data *[]byte) (size int, err error) {
return return
} }
if cap(*data) < size { if cap(*data) < size {
*data = make([]byte, size) *data = client.alloc(size)
} else {
*data = (*data)[:size]
} }
for rest := size; rest > 0; { for rest := size; rest > 0; {
var n int var n int
@ -114,26 +126,18 @@ func (client *RedisClient) readBulkString(data *[]byte) (size int, err error) {
return return
} }
var dataPool = sync.Pool{ func (client *RedisClient) QueryCuboid(cuboid Cuboid, fn func(*Block) *Block) (count int, err error) {
New: func() interface{} {
return make([]byte, 8*1024)
},
}
func (client *RedisClient) QueryCuboid(cuboid Cuboid, fn func(*Block)) (count int, err error) {
p1 := CoordToPlain(cuboid.P1) p1 := CoordToPlain(cuboid.P1)
p2 := CoordToPlain(cuboid.P2) p2 := CoordToPlain(cuboid.P2)
if err = client.writeHSpatial(p1, p2); err != nil { if err = client.writeHSpatial(p1, p2); err != nil {
return return
} }
data := dataPool.Get().([]byte)
defer dataPool.Put(data[:0])
var ( var (
block = Block{} block *Block
size int size int
key int64 key int64
data []byte
) )
for s := client.scratch[:]; ; count++ { for s := client.scratch[:]; ; count++ {
@ -144,15 +148,22 @@ func (client *RedisClient) QueryCuboid(cuboid Cuboid, fn func(*Block)) (count in
if size <= 0 { if size <= 0 {
break break
} }
if key, err = DecodeStringFromBytes((*p)[:size]); err != nil { if key, err = DecodeStringFromBytes(*p); err != nil {
return return
} }
block.Coord = PlainToCoord(key)
if size, err = client.readBulkString(&data); err != nil || size < 0 { if size, err = client.readBulkString(&data); err != nil || size < 0 {
return return
} }
block.Data = data[:size] if block == nil {
fn(&block) block = &Block{Coord: PlainToCoord(key), Data: data}
} else {
*block = Block{Coord: PlainToCoord(key), Data: data}
}
if block = fn(block); block != nil {
data = block.Data[:0]
} else {
data = nil
}
} }
return return

View File

@ -43,40 +43,23 @@ func (yo *YOrder) Reset() {
yo.blocks = blocks[:0] yo.blocks = blocks[:0]
} }
func copyData(data []byte) []byte { func (yo *YOrder) RenderBlock(block *Block) (*Block, error) {
l := len(data)
ndata := make([]byte, l, Max(l, 8*1024))
copy(ndata, data)
return ndata
}
func (yo *YOrder) RenderBlock(block *Block) (err error) {
var nblock *Block
if len(yo.blocks) == yo.capacity { if len(yo.blocks) == yo.capacity {
oblock := yo.blocks[0] oblock := yo.blocks[0]
if oblock.Coord.Y < block.Coord.Y { if oblock.Coord.Y < block.Coord.Y {
// New one is above highest old. Directly render new. // New one is above highest old. Directly render new.
err = yo.RenderFn(block) err := yo.RenderFn(block)
return return block, err
} }
// Render old one. Store copy of new in heap. // Render old one. Store copy of new in heap.
heap.Pop(yo) heap.Pop(yo)
err = yo.RenderFn(oblock) heap.Push(yo, block)
l := len(block.Data) err := yo.RenderFn(oblock)
if cap(oblock.Data) < l { return oblock, err
oblock.Data = make([]byte, l, Max(l, 8*1024))
} else {
oblock.Data = oblock.Data[:l]
}
copy(oblock.Data, block.Data)
oblock.Coord = block.Coord
nblock = oblock
} else {
nblock = &Block{Coord: block.Coord, Data: copyData(block.Data)}
} }
heap.Push(yo, nblock) heap.Push(yo, block)
return return nil, nil
} }
func (yo *YOrder) Drain() error { func (yo *YOrder) Drain() error {