speed up pixel fetching
This commit is contained in:
parent
2a10ebd77d
commit
7283fac957
27
main.go
27
main.go
|
@ -52,18 +52,21 @@ func main() {
|
||||||
fetchedImg := image.NewNRGBA(img.Bounds().Add(offset))
|
fetchedImg := image.NewNRGBA(img.Bounds().Add(offset))
|
||||||
|
|
||||||
if *fetchImgPath != "" {
|
if *fetchImgPath != "" {
|
||||||
// using img.SubImage to distribute tasks is nice, as we can also parallelize command generation easily!
|
fetchCmds := pixelflut.CmdsFetchImage(fetchedImg.Bounds())
|
||||||
// @cleanup: use a box tiling algo instead of hardcoding
|
fetchMessages := fetchCmds.Chunk(1)
|
||||||
b := fetchedImg.Bounds()
|
|
||||||
b1 := image.Rectangle{b.Min, b.Size().Div(2)}
|
{
|
||||||
b2 := b1.Add(image.Pt(b1.Dx(), 0))
|
// @cleanup: encapsulate this in separate function exported from pixelflut
|
||||||
b3 := b1.Add(image.Pt(0, b1.Dy()))
|
conn, err := net.Dial("tcp", *address)
|
||||||
b4 := b1.Add(b1.Size())
|
if err != nil {
|
||||||
go pixelflut.FetchImage(fetchedImg.SubImage(b1).(*image.NRGBA), *address)
|
log.Fatal(err)
|
||||||
go pixelflut.FetchImage(fetchedImg.SubImage(b2).(*image.NRGBA), *address)
|
}
|
||||||
go pixelflut.FetchImage(fetchedImg.SubImage(b3).(*image.NRGBA), *address)
|
// defer conn.Close()
|
||||||
go pixelflut.FetchImage(fetchedImg.SubImage(b4).(*image.NRGBA), *address)
|
|
||||||
*connections -= 4
|
go pixelflut.FetchPixels(fetchedImg, conn)
|
||||||
|
go pixelflut.Bomb2(fetchMessages[0], conn)
|
||||||
|
}
|
||||||
|
*connections -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate and split messages into equal chunks
|
// Generate and split messages into equal chunks
|
||||||
|
|
|
@ -1,57 +1,19 @@
|
||||||
package pixelflut
|
package pixelflut
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/textproto"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func FetchImage(img *image.NRGBA, address string) {
|
func CmdsFetchImage(bounds image.Rectangle) (cmds Commands) {
|
||||||
// FIXME @speed: this is unusably s l o w w w
|
cmds = make([][]byte, bounds.Size().X*bounds.Size().Y)
|
||||||
// bottleneck seems to be our pixel reading/parsing code. cpuprofile!
|
numCmds := 0
|
||||||
// -> should buffer it just as in bomb()
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||||
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||||
conn, err := net.Dial("tcp", address)
|
cmd := fmt.Sprintf("PX %d %d\n", x, y)
|
||||||
if err != nil {
|
cmds[numCmds] = []byte(cmd)
|
||||||
log.Fatal(err)
|
numCmds++
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
reader := bufio.NewReader(conn)
|
|
||||||
tp := textproto.NewReader(reader)
|
|
||||||
|
|
||||||
b := img.Bounds()
|
|
||||||
for {
|
|
||||||
for x := b.Min.X; x < b.Max.X; x++ {
|
|
||||||
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
||||||
// request pixel
|
|
||||||
fmt.Fprintf(conn, "PX %d %d\n", x, y)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read pixel
|
|
||||||
// @speed try to run this in a separate goroutine?
|
|
||||||
// we probably need to buffer the responses then
|
|
||||||
res, err := tp.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
res2 := strings.Split(res, " ")
|
|
||||||
col, _ := hex.DecodeString(res2[3])
|
|
||||||
img.Set(x, y, color.NRGBA{
|
|
||||||
uint8(col[0]),
|
|
||||||
uint8(col[1]),
|
|
||||||
uint8(col[2]),
|
|
||||||
255,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return cmds
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
package pixelflut
|
package pixelflut
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"bufio"
|
||||||
|
"encoding/hex"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @speed: add some performance reporting mechanism on these functions when
|
||||||
|
// called as goroutines
|
||||||
|
|
||||||
// Bomb writes the given message via plain TCP to the given address,
|
// Bomb writes the given message via plain TCP to the given address,
|
||||||
// forever, as fast as possible.
|
// forever, as fast as possible.
|
||||||
func Bomb(message []byte, address string) {
|
func Bomb(message []byte, address string) {
|
||||||
|
@ -14,6 +24,11 @@ func Bomb(message []byte, address string) {
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
Bomb2(message, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @cleanup: find common interface instead of Bomb2
|
||||||
|
func Bomb2(message []byte, conn net.Conn) {
|
||||||
for {
|
for {
|
||||||
_, err := conn.Write(message)
|
_, err := conn.Write(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -21,3 +36,31 @@ func Bomb(message []byte, address string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FetchPixels(target *image.NRGBA, conn net.Conn) {
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
tp := textproto.NewReader(reader)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// @speed: textproto seems not the fastest, buffer text manually & split at \n ?
|
||||||
|
res, err := tp.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @speed: Split is ridiculously slow due to mallocs!
|
||||||
|
// chunk last 6 chars off -> color, remove first 3 chars, find space in
|
||||||
|
// remainder, then Atoi() xy?
|
||||||
|
res2 := strings.Split(res, " ")
|
||||||
|
x, _ := strconv.Atoi(res2[1])
|
||||||
|
y, _ := strconv.Atoi(res2[2])
|
||||||
|
col, _ := hex.DecodeString(res2[3])
|
||||||
|
|
||||||
|
target.Set(x, y, color.NRGBA{
|
||||||
|
uint8(col[0]),
|
||||||
|
uint8(col[1]),
|
||||||
|
uint8(col[2]),
|
||||||
|
255,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ func CommandsFromImage(img image.Image, offset image.Point) (commands Commands)
|
||||||
// @incomplete: also send alpha? -> bandwidth tradeoff
|
// @incomplete: also send alpha? -> bandwidth tradeoff
|
||||||
// @speed: this sprintf call is quite slow..
|
// @speed: this sprintf call is quite slow..
|
||||||
cmd := fmt.Sprintf("PX %d %d %.2x%.2x%.2x\n",
|
cmd := fmt.Sprintf("PX %d %d %.2x%.2x%.2x\n",
|
||||||
x + offset.X, y + offset.Y, c.R, c.G, c.B)
|
x+offset.X, y+offset.Y, c.R, c.G, c.B)
|
||||||
commands[numCmds] = []byte(cmd)
|
commands[numCmds] = []byte(cmd)
|
||||||
numCmds++
|
numCmds++
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue