昨年、NASの一台のディスクを容量拡大のためにSSDからHDDに入れ替えましたが、MacのバックアップがSSD(timemachine)では制限値80%で古いファイルは削除されて追加されているようですが、HDD(Timemachine_2)では80%の壁は乗り越えて増殖中。最後にはおそらく削除は発生すると思うけれども、特に設定は無いように思うけれどもなんでだろう?
P.S. (2023/2/5)
結局どちらの領域も制限地では留まらず、
admin
la vie libre
初めてRustをPlaygroungで動かしてみたけど、結構独特だと思う
“for” loopはイテレータ専用
“The for in construct can be used to iterate through an Iterator. “
通常のループ処理は無限ループ使ってcontinueとbreakで条件判断
<初めてのRustのコード>
fn main() {
let mut i = 0;
loop {
println!("i = {}", i);
if i == 10{
break i;
}
i += 1;
};
}
・mutable変数はmutで宣言が必要、宣言しないとimmutable変数になる
・println!では必ずプレースホルダー”{}”を入れる
・loopの}にも;が必要、if{}の}の後ろにはあっても無くてもコンパイルエラーにはならないけれども
・ifの条件をカッコ()で括らないのはGoと同じ
admin
タイトルの通りですがGo言語は値渡し、例えば関数に引き渡す引数は値が引数のアドレスとは違う場所の関数内で使用する変数のアドレスにコピーされて使われます。
これでは都合悪い時もあるだろうから、参照渡しのためにポインターが存在知ると考えれば良いかもしれない。ポインターはC/C++の記法そのままで、
package main
import "fmt"
func main() {
val := 20
pointer := &val
*pointer = 23
fmt.Println("address : ", pointer, "value : ", val)
}
address : 0x14000126008 value : 23
のように使います、しかしGoでポインターを普段使いしたら、変数がmutableになることでコードの見通しが悪くなるから使うのは限定的だろうと思う。Goでは通常の変数はすべてimmutable扱いだから、逆に言えばポインター宣言は明示的なmutable宣言であるということです。
例にあるようなケースでわざわざポインターを使う必要性はないし、こんな使い方はしちゃいけない例です。数少ない例外はインターフェースを受け取るだけだとは『初めてのGo言語』の記載。
admin
普通はM5Stackで表示内容の書き換えするときに、一度表示内容を消去する必要がありますが、それが表示がちらつく原因です。
ちらつき防止のためには、画面表示内容をフルで別のバッファーに用意しておいて、それを押し込めば表示消去時間が存在しないことになるのでちらつきの防止ができます。
以下のサンプルから、
https://craft-gogo.com/m5stack-sprite/
をそのまま実装してみましたが、確かにちらつきは見えなくなります。
画面書き換えの処理時間を見るために、前後で時間を取得して処理時間を見てみましたが、画面の押し込み(pushSprite())の処理時間は実は普通に画面を書き込む時間と変わらず、大体40msぐらい掛かっています。要は表示領域を一度クリアしないからちらつきを感じることなくなっているわけで、決してpushSprite()にして画面描画時間が高速化される訳ではありません。
admin
ラズパイがネットワーク内に複数台存在しているとmDNSで識別できないから、どれかの変更が必要です。
そのためには、
/etc/hostnameと/etc/hostsを二つとも変更します。
/etc/hostname
/etc/hosts
pi@raspberrypi:~ $ cat /etc/hostname
rasp-b
pi@raspberrypi:~ $ cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.1.1 rasp-b
変更後にリブートすれば、サイドのログオン時には設定したホスト名が使えるようになります。
admin
ダラダラと継続中ですが、とりあえずの現状のまとめです。
<条件>
M1 MacBook air
1000万までの素数計算してスライスに格納、昇順は考慮してない
<言えること>
・goはシングルスレッドでもマルチコアを使うように(並列処理するように)動く、これは便利だ
・channelのオーバーヘッドはmutexよりも大きい予想通りですが、ただしmutexのように個別の変数に対する考慮は不要だからエラーの入りにくいのがchannel
・コア数を強制的にしてしてやるとこのコードの場合には4コアぐらいが最適、もちろん状況により変わりますが
それぞれのソースコードはこちら、
https://github.com/chateight/go/tree/master/concpara
admin
https://isehara-3lv.sakura.ne.jp/blog/2023/01/12/goの並行処理/
ではWaitGroup使って終了を判定しましたが、Go独自の機能にスレッド間での通信機能channelを使う方法もあります。どちらが良いというのではなくて、目的によって使い分けするんだと思いますが。
以下はリンクの処理と同じく一千万までの素数を求める処理です。40行目付近から下のループ処理でchannelを使って素数をスライスに格納しています。channelが存在しないことはタイマーの一秒待ちで判定しています。
package main
import (
"sync"
"time"
"fmt"
)
//
// to use channel instead of "WaitGroup"
//
// in this case, slower than WaitGroup. Channel may be useful when it takes long processing time and less inforamtion size
//
func main() {
maxNumber := 1000*10000
var mu sync.Mutex
ch := make(chan int)
defer close(ch)
c := 2
oddCh := []int{}
tStart := time.Now()
tStop := time.Now()
for i := 2; i <= maxNumber; i++ {
go func() {
flag := true // if odd number, stay "true"
mu.Lock()
defer mu.Unlock()
for j :=2; j*j <= c ; j++ {
if c%j == 0{
flag = false
break
}
}
if flag == true{
ch <- c
}
c++
}()
}
for i:= 0; i < maxNumber; i++{ // set odd numbers to the slice
setBreak := false
select {
case p := <- ch:
oddCh = append(oddCh, p)
tStop = time.Now()
case <- time.After(time.Second): // to check last "ch" data
fmt.Println("Time Out!")
setBreak = true
}
if setBreak == true{
break
}
}
fmt.Println("len : ", len(oddCh))
el := tStop.Sub(tStart)
fmt.Println(el)
}
実はWaitGroupを使った場合に比較して、処理時間は5倍ぐらいになっています。おそらく渡しているのが素数というint形式データを渡しているだけでオーバーヘッドが大きい。したがってchannelを使うのに細かなデータをやり取りするには向かないんだろうと思います。
いずれにしろこのような簡単な処理では、goroutineも使わずにgoの実行環境でマルチコアを使う方が一番高速なのだから、余計なことをやらないのがいちばんのようです。
admin
Goの並行処理は、関数の前にGoを入れるだけで処理対象になります。リソースの排他処理が必要なときには他の言語と同様なmutexを使う、あるいはチャネルを使っても良さそうです。
<素数を求めるコード>
package main
import (
"sync"
"time"
"fmt"
)
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
c := 2
odd := []int{}
tStart := time.Now()
for i := 2; i <= 10000*1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
flag := true // if odd number, stay "true"
mu.Lock()
defer mu.Unlock()
for j :=2; j*j <= c ; j++ {
if c%j == 0{
flag = false
break
}
}
if flag == true{
odd = append(odd, c)
}
c++
}()
}
wg.Wait()
tStop := time.Now()
fmt.Println(len(odd))
el := tStop.Sub(tStart)
fmt.Println(el)
}
mutexを宣言する場所はこのケースではc++の直前でも良さそうなのですが、この位置じゃないとちゃんと結果が出ません。(TBD)
ちなみにC++との実行速度の比較、
千万までの素数計算をM1 Macでさせると、
C++ : およそ1.5秒、Go : およそ6秒と四倍程度遅い。スクリプト言語とは比べようもなく速いのですが。
しかしGoで並行処理をやめて(関数の先頭にgoを付けない)シングルスレッドにすると1.3秒程度で処理完了するから、並行処理にするとオーバーヘッドの分遅くなるだけです。実はGoはシングルスレッド(ネーティブで)でもマルチコアで並列処理を実行するようですね。
—————————————————
さらに以下のコードをM1 MacとRaspberry PI B+(700MHz single core)で実行速度を比較(実行速度向上の点からはほぼ無意味な並行処理)すると、
おおよそラズパイは1/150の実行速度、
admin
Goの標準のライブラリにnet/httpというのがあって、これを使うとwebサーバーが簡単に立ち上げできます。もちろん複雑なことをやるならば他の言語と同じようにフレームワーク(実は単にnet/httpのラッパーらしい)が必要となるのですが。
ともかくも、以下のコードだけでhttp://raspberrypi.local:4000で実行ファイルディレクトリのpubディレクトリにあるindex.htmlの静的ページを返します。別にラズパイ以外でも同じなのですが、実際に使うのはラズパイだろうからラズパイでやっています。
package main
import (
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("pub"))
http.Handle("/", fs)
http.ListenAndServe(":4000", nil)
}
ブラウザからアクセスするとこんな感じです、スタイルは未指定。
今時の言語ではwebサーバーは特別に分離しないで言語と一体化が自然な流れになってきています。
admin
Go言語の特徴の一つだと思いますが、クロス環境のバイナリを環境変数の指定で作成できること。
例えば
Raspberry PIのような決して早くはないハード用のバイナリをMacで作成するのは現実的だろうと思う。
<hello_go.go>
package main
import "fmt"
func main(){
fmt.Println("hello Go", 2*3)
}
のシンプルなソースを環境変数指定でビルドします。Raspberry PIの環境変数は、
$ go env
GO111MODULE=""
GOARCH="arm"
GOBIN=""
GOCACHE="/home/pi/.cache/go-build"
GOENV="/home/pi/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/pi/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/pi/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_arm"
GOVCS=""
GOVERSION="go1.19.4"
GCCGO="gccgo"
GOARM="6"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/pi/go_prj/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -marm -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1086455489=/tmp/go-build -gno-record-gcc-switches"
必要なのは、上のリストでGOOS=linux GOARCH=arm GOARM=6の部分、
ビルドを以下のコマンドで実施して、
$ GOOS=linux GOARCH=arm GOARM=6 go build hello_go.go
scpでRaspberry PIに転送でうまく実行できました、最初どこかのページでGOARM=7になっていたので、それで実行すると、
$ ./hello_go
Illegal instruction
と言われたので、ラズパイ環境からそのまま使用するのが間違いないでしょう。当然ラズパイでもバージョンで異なるはずですが、ここで使ったのはかなり初期の素のラズパイB+です。
ただし、ネーティブ環境よりはビルド時間遅くなっています。これはある意味当然かもしれませんが、それでもラズパイでビルドするよりはずっとマシだと思います。
admin