Skip to content

Commit

Permalink
examples/bench for benchmarking -- not scaling well at larger sizes r…
Browse files Browse the repository at this point in the history
…elative to C++, but getting some threading advantages. added patgen lib, permute util funcs, timer package, and first-pass layer-level threading.
  • Loading branch information
Randall C. O'Reilly committed Feb 3, 2019
1 parent 033d04f commit 36030eb
Show file tree
Hide file tree
Showing 19 changed files with 725 additions and 45 deletions.
4 changes: 2 additions & 2 deletions basic/leabra/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func TestMakeNet(t *testing.T) {
outHid.Class = "TopDown"

TestNet.Defaults()
TestNet.StyleParams(Pars[0])
TestNet.StyleParams(Pars[0], false) // no msg
TestNet.Build()
TestNet.InitWts()
TestNet.TrialInit() // get GeScale
Expand Down Expand Up @@ -280,7 +280,7 @@ func TestNetLearn(t *testing.T) {

for ti := 0; ti < NLrnPars; ti++ {
TestNet.Defaults()
TestNet.StyleParams(Pars[ti])
TestNet.StyleParams(Pars[ti], false) // no msg
TestNet.InitWts()
TestNet.InitExt()

Expand Down
22 changes: 14 additions & 8 deletions basic/leabra/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,29 +152,33 @@ func (ly *Layer) UpdateParams() {
}

// SetParams sets given parameters to this layer, if the target type is Layer
// calls UpdateParams to ensure derived parameters are all updated
func (ly *Layer) SetParams(pars emer.Params) bool {
// calls UpdateParams to ensure derived parameters are all updated.
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
func (ly *Layer) SetParams(pars emer.Params, setMsg bool) bool {
trg := pars.Target()
if trg != "Layer" {
return false
}
pars.Set(ly)
pars.Set(ly, setMsg)
ly.UpdateParams()
return true
}

// StyleParam applies a given style to either this layer or the receiving projections in this layer
// depending on the style specification (.Class, #Name, Type) and target value of params.
// returns true if applied successfully.
func (ly *Layer) StyleParam(sty string, pars emer.Params) bool {
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
func (ly *Layer) StyleParam(sty string, pars emer.Params, setMsg bool) bool {
if emer.StyleMatch(sty, ly.Name, ly.Class, "Layer") {
if ly.SetParams(pars) {
if ly.SetParams(pars, setMsg) {
return true // done -- otherwise, might be for prjns
}
}
set := false
for _, pj := range ly.RecvPrjns {
did := pj.(*Prjn).StyleParam(sty, pars) // note: could add to emer interface
did := pj.(*Prjn).StyleParam(sty, pars, setMsg) // note: could add to emer interface
if did {
set = true
}
Expand All @@ -184,9 +188,11 @@ func (ly *Layer) StyleParam(sty string, pars emer.Params) bool {

// StyleParams applies a given styles to either this layer or the receiving projections in this layer
// depending on the style specification (.Class, #Name, Type) and target value of params
func (ly *Layer) StyleParams(psty emer.ParamStyle) {
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
func (ly *Layer) StyleParams(psty emer.ParamStyle, setMsg bool) {
for sty, pars := range psty {
ly.StyleParam(sty, pars)
ly.StyleParam(sty, pars, setMsg)
}
}

Expand Down
50 changes: 42 additions & 8 deletions basic/leabra/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/emer/emergent/emer"
"github.com/emer/emergent/prjn"
"github.com/emer/emergent/timer"
"github.com/goki/gi/gi"
"github.com/goki/ki/indent"
"github.com/goki/ki/ints"
Expand All @@ -25,7 +26,8 @@ type NetworkStru struct {
LayMap map[string]emer.Layer `desc:"map of name to layers -- layer names must be unique"`

NThreads int `inactive:"+" desc:"number of parallel threads (go routines) to use -- this is computed directly from the Layers which you must explicitly allocate to different threads -- updated during Build of network"`
LayThr [][]emer.Layer `inactive:"+" desc:"layers per thread -- outer group is threads and inner is layers operated on by that thread -- based on user-assigned threads, initialized during Build"`
ThrLay [][]emer.Layer `inactive:"+" desc:"layers per thread -- outer group is threads and inner is layers operated on by that thread -- based on user-assigned threads, initialized during Build"`
ThrTimes []timer.Time `desc:"timers for each thread, so you can see how evenly the workload is being distributed"`
}

// emer.Network interface methods:
Expand Down Expand Up @@ -74,16 +76,17 @@ func (nt *NetworkStru) BuildThreads() {
nthr = ints.MaxInt(nthr, ly.LayThread())
}
nt.NThreads = nthr + 1
nt.LayThr = make([][]emer.Layer, nt.NThreads)
nt.ThrLay = make([][]emer.Layer, nt.NThreads)
nt.ThrTimes = make([]timer.Time, nt.NThreads)
for _, ly := range nt.Layers {
if ly.IsOff() {
continue
}
th := ly.LayThread()
nt.LayThr[th] = append(nt.LayThr[th], ly)
nt.ThrLay[th] = append(nt.ThrLay[th], ly)
}
for th := 0; th < nt.NThreads; th++ {
if len(nt.LayThr[th]) == 0 {
if len(nt.ThrLay[th]) == 0 {
log.Printf("Network BuildThreads: Network %v has no layers for thread: %v\n", nt.Name, th)
}
}
Expand Down Expand Up @@ -135,9 +138,11 @@ func (nt *Network) UpdateParams() {

// StyleParams applies a given styles to layers and receiving projections,
// depending on the style specification (.Class, #Name, Type) and target value of params
func (nt *Network) StyleParams(psty emer.ParamStyle) {
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
func (nt *Network) StyleParams(psty emer.ParamStyle, setMsg bool) {
for _, ly := range nt.Layers {
ly.StyleParams(psty)
ly.StyleParams(psty, setMsg)
}
}

Expand Down Expand Up @@ -333,7 +338,8 @@ func (nt *Network) Cycle() {
nt.AvgMaxAct()
}

// ThrLayFun calls function on layer, using threaded (go routine) computation
// ThrLayFun calls function on layer, using threaded (go routine) computation if NThreads > 1
// and otherwise just iterates over layers in the current thread.
func (nt *Network) ThrLayFun(fun func(ly *Layer)) {
if nt.NThreads <= 1 {
for _, ly := range nt.Layers {
Expand All @@ -347,20 +353,48 @@ func (nt *Network) ThrLayFun(fun func(ly *Layer)) {
for th := 0; th < nt.NThreads; th++ {
wg.Add(1)
go func(tt int) {
thly := nt.LayThr[tt]
thly := nt.ThrLay[tt]
nt.ThrTimes[tt].Start()
for _, ly := range thly {
if ly.IsOff() {
continue
}
fun(ly.(*Layer))
}
nt.ThrTimes[tt].Stop()
wg.Done()
}(th)
}
wg.Wait()
}
}

// ThrTimerReport reports the amount of time spent in each thread
func (nt *Network) ThrTimerReport() {
if nt.NThreads <= 1 {
fmt.Printf("ThrTimerReport: not running multiple threads\n")
return
}
fmt.Printf("ThrTimerReport: %v, NThreads: %v\n", nt.Name, nt.NThreads)
fmt.Printf("\tThr\tTotal Secs\tPct\n")
pcts := make([]float64, nt.NThreads)
tot := 0.0
for th := 0; th < nt.NThreads; th++ {
pcts[th] = nt.ThrTimes[th].TotalSecs()
tot += pcts[th]
}
for th := 0; th < nt.NThreads; th++ {
fmt.Printf("\t%v \t%6g\t%6g\n", th, pcts[th], pcts[th]/tot)
}
}

// ThrTimerReset resets the per-thread timers
func (nt *Network) ThrTimerReset() {
for th := 0; th < nt.NThreads; th++ {
nt.ThrTimes[th].Reset()
}
}

// SendGeDelta sends change in activation since last sent, if above thresholds
// and integrates sent deltas into GeRaw and time-integrated Ge values
func (nt *Network) SendGeDelta() {
Expand Down
20 changes: 13 additions & 7 deletions basic/leabra/prjn.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,31 +233,37 @@ func (pj *Prjn) UpdateParams() {
}

// SetParams sets given parameters to this prjn, if the target type is Prjn
// calls UpdateParams to ensure derived parameters are all updated
func (pj *Prjn) SetParams(pars emer.Params) bool {
// calls UpdateParams to ensure derived parameters are all updated.
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
func (pj *Prjn) SetParams(pars emer.Params, setMsg bool) bool {
trg := pars.Target()
if trg != "Prjn" {
return false
}
pars.Set(pj)
pars.Set(pj, setMsg)
pj.UpdateParams()
return true
}

// StyleParam applies a given style to this projection
// depending on the style specification (.Class, #Name, Type) and target value of params
func (pj *Prjn) StyleParam(sty string, pars emer.Params) bool {
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
func (pj *Prjn) StyleParam(sty string, pars emer.Params, setMsg bool) bool {
if emer.StyleMatch(sty, pj.PrjnName(), pj.Class, "Prjn") {
return pj.SetParams(pars)
return pj.SetParams(pars, setMsg)
}
return false
}

// StyleParams applies a given styles to either this prjn
// depending on the style specification (.Class, #Name, Type) and target value of params
func (pj *Prjn) StyleParams(psty emer.ParamStyle) {
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
func (pj *Prjn) StyleParams(psty emer.ParamStyle, setMsg bool) {
for sty, pars := range psty {
pj.StyleParam(sty, pars)
pj.StyleParam(sty, pars, setMsg)
}
}

Expand Down
4 changes: 3 additions & 1 deletion emer/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,7 @@ type Layer interface {
UpdateParams()

// StyleParams applies a given ParamStyle style sheet to the layer and recv projections
StyleParams(psty ParamStyle)
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
StyleParams(psty ParamStyle, setMsg bool)
}
4 changes: 3 additions & 1 deletion emer/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ type Network interface {
UpdateParams()

// StyleParams applies a given ParamStyle style sheet to the layers and projections in network
StyleParams(psty ParamStyle)
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
StyleParams(psty ParamStyle, setMsg bool)

// WriteWtsJSON writes network weights (and any other state that adapts with learning)
// to JSON-formatted output
Expand Down
9 changes: 6 additions & 3 deletions emer/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ func (pr *Params) Path(path string) string {

// Set applies all parameter values to given object.
// object must already be the appropriate target type based on the first element of the path
// (see Target method)
func (pr *Params) Set(obj interface{}) {
// (see Target method). if setMsg is true, then it will print a confirmation that the parameter
// was set (it always prints an error message if it fails to set the parameter at given path)
func (pr *Params) Set(obj interface{}, setMsg bool) {
olbl := ""
olblr, haslbl := obj.(gi.Labeler)
if haslbl {
Expand All @@ -114,7 +115,9 @@ func (pr *Params) Set(obj interface{}) {
path := pr.Path(pt)
ok := SetParam(obj, path, v)
if ok {
log.Printf("%v Set param path: %v to value: %v\n", olbl, pt, v)
if setMsg {
log.Printf("%v Set param path: %v to value: %v\n", olbl, pt, v)
}
} else {
log.Printf("%v Failed to set param path: %v to value: %v\n", olbl, pt, v)
}
Expand Down
4 changes: 3 additions & 1 deletion emer/prjn.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ type Prjn interface {
UpdateParams()

// StyleParams applies a given ParamStyle style sheet to the projections
StyleParams(psty ParamStyle)
// If setMsg is true, then a message is printed to confirm each parameter that is set.
// it always prints a message if a parameter fails to be set.
StyleParams(psty ParamStyle, setMsg bool)
}

// PrjnList is a slice of projections
Expand Down
11 changes: 11 additions & 0 deletions erand/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) 2019, The Emergent Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package erand provides randomization functionality built on top of standard math/rand
// random number generation functions. Includes:
// * RndParams: specifies parameters for random number generation according to various distributions
// used e.g., for initializing random weights and generating random noise in neurons
// * Permute*: basic convenience methods calling rand.Shuffle on e.g., []int slice
//
package erand
47 changes: 47 additions & 0 deletions erand/permute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2019, The Emergent Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package erand

import "math/rand"

// PermuteInts permutes (shuffles) the order of elements in the given int slice
// using the standard Fisher-Yates shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
// So you don't have to remember how to call rand.Shuffle
func PermuteInts(is []int) {
rand.Shuffle(len(is), func(i, j int) {
is[i], is[j] = is[j], is[i]
})
}

// PermuteStrings permutes (shuffles) the order of elements in the given string slice
// using the standard Fisher-Yates shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
// So you don't have to remember how to call rand.Shuffle
func PermuteStrings(is []string) {
rand.Shuffle(len(is), func(i, j int) {
is[i], is[j] = is[j], is[i]
})
}

// PermuteFloat32s permutes (shuffles) the order of elements in the given float32 slice
// using the standard Fisher-Yates shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
// So you don't have to remember how to call rand.Shuffle
func Permutefloat32s(is []float32) {
rand.Shuffle(len(is), func(i, j int) {
is[i], is[j] = is[j], is[i]
})
}

// PermuteFloat64s permutes (shuffles) the order of elements in the given float64 slice
// using the standard Fisher-Yates shuffle
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
// So you don't have to remember how to call rand.Shuffle
func Permutefloat64s(is []float64) {
rand.Shuffle(len(is), func(i, j int) {
is[i], is[j] = is[j], is[i]
})
}
Binary file added examples/bench/bench
Binary file not shown.
Loading

0 comments on commit 36030eb

Please sign in to comment.