読者です 読者をやめる 読者になる 読者になる

niconegoto Blog

niconegoto's journal

GoConで発表してきたのでついでにruntime以下の知識をまとめていく #golang

3/25に行われたGoConで"How Communicating Sequential Goroutines Work"という発表をしてきました。 当初僕はCommunicating Sequential Processesについての話しをする予定だったのですが、時間内にとても発表できそうな内容ではなかったため、Concurrency全般についての話をしました。 そのため、ここではその際触れられなかったgoroutineの実装の話しやCSPの話しなどを含めてGoのruntimeについて何回かに分けてまとめていきたいと思います。今回は主にgoroutineについてです。

GoのConcurrency

goroutineの説明に入る前にざっくりGoのConcurrencyについて説明します。

以下、GoConでの発表スライドにざっくりと沿いながら書いていきます。 speakerdeck.com

Goの有名な格言として

“Do not communicate by sharing memory; instead, share memory by communicating.”

というものがあるのはみなさんご存じでしょう。これはgo-proverbsというGoらしいコードを書くためのことわざ集にも書いてあり、とても有名な格言かと思います。

この格言はConcurrencyを実現する上で一般的に用いられるShared-memory CommunicationとMessaging-passing Communicationの2つのモデルを並べて、Messaging-passing Communicationしようねと言っています。ただ、この格言を理解するためにはShared-memory CommunicationとMessaging-passing Communicationそれぞれの特徴とGoにおいてそれぞれの手法がどのように実現されているのかを理解しなければいけません。

Shared-memory CommunicationとMessaging-passing Communicationの違いなどは発表資料を参考にしてください。

Goの実装

Messaging-passing Communication

では、GoではMessaging-passing Communicationをどのように実現しているんでしょうか。 ポイントとなるのは以下の機能です。

  • goroutine 2048byteの軽量なスレッドのようなもの
  • channel goroutine 間でのメッセージパッシングを行う
  • select 複数の channel の受信を同時に行う場合などに用いる

これらの機能を用いることによってGoではMessaging-passing Communicationをより簡単に使えるようになっています。 今回の記事ではこの中のgoroutineに焦点を当てます。(続編でchannelについても扱う予定です)

なお、発表でも触れましたがselectの実践的な使い方に関しては牧さんの以下の発表が詳しいです。

www.slideshare.net

Shared-memory Communication

GoではShared-memory Communicationを実現するための方法としてsync.Mutexなどが提供されています。ただ、今回はMessaging-passingに焦点を当てるためここではあまり深くは扱いません。 詳しくはドキュメントを読んでください、と言いたいところではありますが英語に抵抗がある方などは、少し前の記事にはなりますがmattnさんによるsyncパッケージについての記事などもあるのでそちらを読むといいと思います。 mattn.kaoriya.net

goroutineの実装

さて、やっと本題のgoroutineについてです。 goroutineを知るためにはsrc/runtime以下を読まなければいけません。runtimeのドキュメントは以下です。 runtime - The Go Programming Language

goroutineとはなんぞや?

軽量なスレッドのようなものですが、goroutineは最小で2048byteなのでWindows だと1MB、Linux だと2MB であるスレッドのデフォルトスタックサイズにくらべてとても軽量です。

また、OSスレッドはOSカーネルでスケジュールされており、スレッド間の制御を変更するには完全なコンテキストスイッチが必要なので遅くなってしまうのですが、goroutineは以下で説明するM:Nスレッド(LWP方式)を用いているため、スレッドの再スケジュールより低コストにスケジューリング可能という特徴があります。

スレッドの種類と特徴

スレッドには大きく分けてN:11:1、そしてM:N方式があります。

N:1

複数のユーザー空間スレッドが1つのOSスレッドで実行されます

1:1

1つの実行スレッドが1つのOSスレッドと一致します

  • 長所 マシン上のすべてのコアを利用できる

  • 短所 トラップ(強制的割り込み)する必要があるため、コンテキストスイッチが遅い

M:N

任意の数のOSスレッドに任意の数のゴルーチンをスケジューリングします。

  • 長所 コンテキストスイッチをすばやく実行し、システム内のすべてのコアを活用できる

  • 短所 いいとこ取りだけどスケジューラーへの追加が煩雑でつらい

それぞれこれらのような特徴があるのですが、GoではM:N方式を採用しつつ、欠点であるスケジューラーへの追加の煩雑さをユーザーが意識せずに使えるようにしています。

登場人物説明

以下runtime以下の説明をしていくにあたって主要な登場人物が3人(!?)います。

G

type g struct {
    // Stack parameters.
    // stack describes the actual stack memory: [stack.lo, stack.hi).
    // stackguard0 is the stack pointer compared in the Go stack growth prologue.
    // It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
    // stackguard1 is the stack pointer compared in the C stack growth prologue.
    // It is stack.lo+StackGuard on g0 and gsignal stacks.
    // It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
    stack       stack   // offset known to runtime/cgo
    stackguard0 uintptr // offset known to liblink
    stackguard1 uintptr // offset known to liblink

    _panic         *_panic // innermost panic - offset known to liblink
    _defer         *_defer // innermost defer
    m              *m      // current m; offset known to arm liblink
    sched          gobuf
    syscallsp      uintptr        // if status==Gsyscall, syscallsp = sched.sp to use during gc
    syscallpc      uintptr        // if status==Gsyscall, syscallpc = sched.pc to use during gc
    stktopsp       uintptr        // expected sp at top of stack, to check in traceback
    param          unsafe.Pointer // passed parameter on wakeup
    atomicstatus   uint32
    stackLock      uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
    goid           int64
    waitsince      int64  // approx time when the g become blocked
    waitreason     string // if status==Gwaiting
    schedlink      guintptr
    preempt        bool     // preemption signal, duplicates stackguard0 = stackpreempt
    paniconfault   bool     // panic (instead of crash) on unexpected fault address
    preemptscan    bool     // preempted g does scan for gc
    gcscandone     bool     // g has scanned stack; protected by _Gscan bit in status
    gcscanvalid    bool     // false at start of gc cycle, true if G has not run since last scan; TODO: remove?
    throwsplit     bool     // must not split stack
    raceignore     int8     // ignore race detection events
    sysblocktraced bool     // StartTrace has emitted EvGoInSyscall about this goroutine
    sysexitticks   int64    // cputicks when syscall has returned (for tracing)
    traceseq       uint64   // trace event sequencer
    tracelastp     puintptr // last P emitted an event for this goroutine
    lockedm        *m
    sig            uint32
    writebuf       []byte
    sigcode0       uintptr
    sigcode1       uintptr
    sigpc          uintptr
    gopc           uintptr // pc of go statement that created this goroutine
    startpc        uintptr // pc of goroutine function
    racectx        uintptr
    waiting        *sudog         // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
    cgoCtxt        []uintptr      // cgo traceback context
    labels         unsafe.Pointer // profiler labels
    timer          *timer         // cached timer for time.Sleep

    // Per-G GC state

    // gcAssistBytes is this G's GC assist credit in terms of
    // bytes allocated. If this is positive, then the G has credit
    // to allocate gcAssistBytes bytes without assisting. If this
    // is negative, then the G must correct this by performing
    // scan work. We track this in bytes to make it fast to update
    // and check for debt in the malloc hot path. The assist ratio
    // determines how this corresponds to scan work debt.
    gcAssistBytes int64
}

はい。この子がG(Goroutine)です。 g型で表されます。 goroutineが終了すると、その gは空いているgのpoolに戻されて、後で他のゴルーチンのために再利用されます。 スタック内には、命令ポインタおよびゴルーチンのスケジューリングに重要な情報が含まれます。(例:ブロックされている可能性があるチャネルの情報等)

ちなみにこれらはsrc/runtime/runtime2.goというファイルに記述されています。 runtime以下には他にruntime.goruntime1.goというファイルが存在します。歴史を感じますね…

M

type m struct {
    g0      *g     // goroutine with scheduling stack
    morebuf gobuf  // gobuf arg to morestack
    divmod  uint32 // div/mod denominator for arm - known to liblink

    // Fields not known to debuggers.
    procid        uint64     // for debuggers, but offset not hard-coded
    gsignal       *g         // signal-handling g
    sigmask       sigset     // storage for saved signal mask
    tls           [6]uintptr // thread-local storage (for x86 extern register)
    mstartfn      func()
    curg          *g       // current running goroutine
    caughtsig     guintptr // goroutine running during fatal signal
    p             puintptr // attached p for executing go code (nil if not executing go code)
    nextp         puintptr
    id            int32
    mallocing     int32
    throwing      int32
    preemptoff    string // if != "", keep curg running on this m
    locks         int32
    softfloat     int32
    dying         int32
    profilehz     int32
    helpgc        int32
    spinning      bool // m is out of work and is actively looking for work
    blocked       bool // m is blocked on a note
    inwb          bool // m is executing a write barrier
    newSigstack   bool // minit on C thread called sigaltstack
    printlock     int8
    incgo         bool // m is executing a cgo call
    fastrand      uint32
    ncgocall      uint64      // number of cgo calls in total
    ncgo          int32       // number of cgo calls currently in progress
    cgoCallersUse uint32      // if non-zero, cgoCallers in use temporarily
    cgoCallers    *cgoCallers // cgo traceback if crashing in cgo call
    park          note
    alllink       *m // on allm
    schedlink     muintptr
    mcache        *mcache
    lockedg       *g
    createstack   [32]uintptr // stack that created this thread.
    freglo        [16]uint32  // d[i] lsb and f[i]
    freghi        [16]uint32  // d[i] msb and f[i+16]
    fflag         uint32      // floating point compare flags
    locked        uint32      // tracking for lockosthread
    nextwaitm     uintptr     // next m waiting for lock
    needextram    bool
    traceback     uint8
    waitunlockf   unsafe.Pointer // todo go func(*g, unsafe.pointer) bool
    waitlock      unsafe.Pointer
    waittraceev   byte
    waittraceskip int
    startingtrace bool
    syscalltick   uint32
    thread        uintptr // thread handle

    // these are here because they are too large to be on the stack
    // of low-level NOSPLIT functions.
    libcall   libcall
    libcallpc uintptr // for cpu profiler
    libcallsp uintptr
    libcallg  guintptr
    syscall   libcall // stores syscall parameters on windows

    mOS
}

M(Machine)です。 OSスレッドを表します。これはOSによって管理される実行スレッドであり、標準のPOSIXスレッドのようなもので mで表されます。 ユーザーのGoコード、runtimeのコード、syscallを実行しているか、idle状態になっています。複数のスレッドがシステムコールでブロックされる可能性があるため、一度に複数のMが存在する可能性があります。

P

type p struct {
    lock mutex

    id          int32
    status      uint32 // one of pidle/prunning/...
    link        puintptr
    schedtick   uint32   // incremented on every scheduler call
    syscalltick uint32   // incremented on every system call
    m           muintptr // back-link to associated m (nil if idle)
    mcache      *mcache
    racectx     uintptr

    deferpool    [5][]*_defer // pool of available defer structs of different sizes (see panic.go)
    deferpoolbuf [5][32]*_defer

    // Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen.
    goidcache    uint64
    goidcacheend uint64

    // Queue of runnable goroutines. Accessed without lock.
    runqhead uint32
    runqtail uint32
    runq     [256]guintptr
    // runnext, if non-nil, is a runnable G that was ready'd by
    // the current G and should be run next instead of what's in
    // runq if there's time remaining in the running G's time
    // slice. It will inherit the time left in the current time
    // slice. If a set of goroutines is locked in a
    // communicate-and-wait pattern, this schedules that set as a
    // unit and eliminates the (potentially large) scheduling
    // latency that otherwise arises from adding the ready'd
    // goroutines to the end of the run queue.
    runnext guintptr

    // Available G's (status == Gdead)
    gfree    *g
    gfreecnt int32

    sudogcache []*sudog
    sudogbuf   [128]*sudog

    tracebuf traceBufPtr

    palloc persistentAlloc // per-P to avoid mutex

    // Per-P GC state
    gcAssistTime     int64 // Nanoseconds in assistAlloc
    gcBgMarkWorker   guintptr
    gcMarkWorkerMode gcMarkWorkerMode

    // gcw is this P's GC work buffer cache. The work buffer is
    // filled by write barriers, drained by mutator assists, and
    // disposed on certain GC state transitions.
    gcw gcWork

    runSafePointFn uint32 // if 1, run sched.safePointFn at next safe point

    pad [sys.CacheLineSize]byte
}

P(processor)です。 スケジューラおよびメモリアロケータの状態など、ユーザGoコードを実行するために必要なリソースを表し、pと書かれます。

単一のスレッドでGoコードを実行するスケジューラのローカライズ版のようなものだとイメージしてください。つまり、pの内容はCPUごとの状態のように考えることができ、スレッド単位またはゴルーチン単位である必要はない状態を管理するのに適しています。 N:1スケジューラからM:Nスケジューラに移行することができる重要な部分です。

Pによって、Goプロセスの呼び出しを個々のコンピュータに合わせることができます。(4コアPCであれば、4つのスレッドでGoコードを実行するようになります) すごい子なんです。 

Pの数は起動時にGOMAXPROCS環境変数の値または実行時関数GOMAXPROCS()によって設定されます。 あとでも説明しますがGOMAXPROCSはあくまでもPの値であり、Mではありません。つまり、GOMAXPROCSが1でも複数のOSスレッドで実行されることはあります

スケジューラー

GとMとPがただ存在するだけでは我々の使っている並行処理機構は成立しえません。 実行するコードであるG、実行する場所であるM、それを実行する権利とリソースであるPをうまく組み合わせてあげる必要があります。 そこでスケジューラーの出番です。

ユーザースタックとシステムスタック

activeなGにはGoコードが実行する最小で2048byteのユーザースタックが関連付けられていて、動的に増減します。

全てのMにはそのユーザースタックにに関連するシステムスタックがstub Gとして実装されており、Mの “g0"スタックと呼ばれており、UnixではシグナルスタックがMの "gsignal"スタックとして実装されています。システムスタックとシグナルスタックは大きく拡大することはできませんが、ランタイムとcgoコードを実行するのに十分な大きさがあります。

runtimeのコードはsystemstack mcall asmcgocallなどを使用して、一時的にシステムスタックに切り替えてユーザーのゴルーチンを切り替えるタスクを実行します。システムスタック上で処理が実行されている間はユーザースタックは実行に使用されません。

goroutineが切り替わるタイミング

上記のタイミングでgoroutineの切り替え作業が行われるのですが、ざっくりとまとめると以下のようなタイミングで切り替えが行われます。

  • アンバッファなチャネルへの読み書きが行われる
  • システムコールが呼ばれる ディスクI/Oとか待ちが入る余地がない即座に帰ってくる系のものだとスイッチしません
  • メモリの割り当てが行われる
  • time.Sleep()が呼ばれる
  • runtime.Gosched()が呼ばれる

GAEのようなGOMAXPROCS(=P)が1の時にこの切り替えが呼ばれない処理を並行処理しようとしても逐次処理と同じ動作をしてしまうので注意が必要です。

同期方法

runtimeには複数の同期メカニズムがあります。セマンティクス、特にゴルーチンスケジューラまたはOSスケジューラと相互作用するかどうかが異なります。

mutex

ロックとアンロックを使う単純な方法で、共有部分を短期間保護するために使用されます。 mutexを用いるとGoスケジューラとやりとりすることなくMが直接ブロックされるのでruntimeの最下位レベルから使用する分には安全ですが、関連付けられたGおよびPの再スケジューリングもブロックされてしまいます。

note

notesleepnotewakeupというメソッドを持つnoteを使用するone-shot notificationsという手法があります。notesleepは、関連付けられたGとPの再スケジュールをブロックしてしまいますが、notetsleepgはブロックシステムコールのように動作し、別のGを実行するためにPを再利用できるようにします。こちらの手法はMを消費するのでGを直接ブロックする方法よりは効率的ではありません。

つまり、以下の様にまとめられます

Interface G M P
mutex Y Y Y
note Y Y |Y/N
park Y N N

動作例

イメージが湧きやすいように例を示します。以下の図はThe Go scheduler - Morsing's blogから引用したものです。 G,M,Pはそれぞれ以下の様なアイコンで表します。 f:id:niconegoto:20170411085818p:plain

通常状態(GOMAXPROCS=2)

f:id:niconegoto:20170411085813p:plain

青色のGが実行中のGで、PはGo文が実行されるたびにrunqueuesというキューのリストからGをポップします。GOMAXPROCS=2なのでPは2つ存在します。 Pがスケジューリングポイント(メモリが一貫性をもつポイント)までゴルーチンを実行すると、その実行キューからGがポップされ、スタックと命令ポインタが設定され、ゴルーチンの実行が開始されます。このPの持つGのリストをローカルの実行キューと呼び、これ以外にグローバルの実行キューが存在します。 Pはローカル実行キューを使い果たしたときにグローバルキューからGを引き出します。 旧バージョンのGoスケジューラではグローバル実行キューしかありませんでしたが、1.1からローカル実行キューが追加されました。

Pによるsteal

以下の図の左側では、片方のPからGが無くなって手持ちぶさたになってしまっています。

f:id:niconegoto:20170411085823p:plain

待機しているGが無くなるとPは別のPから実行キューの約半分を奪います。 これによって、コンテキストごとに常に作業が行われるようになり、すべてのスレッドが最大容量で動作することができます。

syscall

以下はG0でsyscallが呼ばれた場合です。

f:id:niconegoto:20170411085754p:plain

Mはコードを実行していてシステムコールでブロックすることができないため、スケジューリングを維持できるようにPをハンドオフする必要があります。 M0はシステムコールを作成したgoroutineを保持したままPをリストにプッシュして、それをM1がポップして使用します。その状況が右図です。 この左図から、GOMAXPROCS(=P)が1であってもGoプログラムは複数のスレッド(M)で実行されることがわかります。

最後に

いかがだったでしょうか。 今回、メモリ割り当てやGCコンパイラ命令の話しはあまり需要がなさそうなので触れませんでしたが、runtimeがGoの奥深さが詰まった世界であることが少しでも伝わったら幸いです。 runtimeを読んでGoにコミットをしてみるのも楽しいかなと思います。 ちなみに、ランタイムエラーのデバッグでは、GOTRACEBACK = systemGOTRACEBACK = crashで実行すると便利です。

現在続編としてchannelとCSPに関する記事を書いています。ゆっくりにはなりますが心が折れない限り書くのでお待ちください… This Week in Go commitsの方も二週間空いてしまいそうなので、気が向いたら更新したいです。

参考資料

This Week In Go commits(2017/3/20〜) #golang

3/20~3/28のコミットから取り上げています。 中国や京都に旅行していたらコミットが溜まってしまっていました…

cmd/compile: don’t merge load+op if other op arg is still live

#19595に関連する変更です。 通常であればloadop

    l = LOAD ptr mem
    y = OP x l

into

    y = OPload x ptr mem

と、1つの命令に統合したいのですが、すべてのOPload命令ではyxと同じレジスタを使用する必要があります。 xがこの命令の後に必要な場合は、xを他の場所にコピーしなければならず、命令を最初に統合する利点が失われます。そのため、他のop引数がまだ生きていれば、上記の最適化を無効にしてloadopを統合しないようにしています。

Commit Message

cmd/compile: don't merge load+op if other op arg is still live
We want to merge a load and op into a single instruction

    l = LOAD ptr mem
    y = OP x l

into

    y = OPload x ptr mem

However, all of our OPload instructions require that y uses
the same register as x. If x is needed past this instruction, then
we must copy x somewhere else, losing the whole benefit of merging
the instructions in the first place.

Disable this optimization if x is live past the OP.

Also disable this optimization if the OP is in a deeper loop than the load.

Update #19595

Change-Id: I87f596aad7e91c9127bfb4705cbae47106e1e77a
Reviewed-on: https://go-review.googlesource.com/38337
Reviewed-by: Ilya Tocar <ilya.tocar@intel.com>

net/rpc: Create empty maps and slices as return type

#19588に対する修正です。

package main

import (
    "fmt"
    "log"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
)

type Arith int

type Result int

func (t *Arith) Multiply(args int, result *Result) error {
    return nil
}

func (t *Arith) DoMap(args int, result *map[string]string) error {
    *result = map[string]string{}
    return nil
}

func (t *Arith) DoMap2(args int, result *map[string]string) error {
    return nil
}

func main() {
    go runServer()
    client, err := jsonrpc.Dial("tcp", fmt.Sprintf("127.0.0.1:9001"))
    if err != nil {
        log.Fatal(err.Error())
    }

    var reply Result
    err = client.Call("Arith.Multiply", 1, &reply)
    if err != nil {
        log.Fatal(err.Error())
    }
    fmt.Println("call Multiply success")
    var ret map[string]string
    err = client.Call("Arith.DoMap", 1, &ret)
    if err != nil {
        log.Fatal(err.Error())
    }
    fmt.Println("call DoMap success")
    err = client.Call("Arith.DoMap2", 1, &ret)
    if err != nil {
        log.Fatal(err.Error())
    }
    fmt.Println("call DoMap2 success")
}

func runServer() {
    arith := new(Arith)
    rpc.Register(arith)
    tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9001")
    if err != nil {
        fmt.Println("ResolveTCPAddr error:", tcpAddr, err.Error())
        return
    }

    listener, err := net.ListenTCP("tcp", tcpAddr)
    if err != nil {
        fmt.Println("listen tcp error:", tcpAddr, err.Error())
        return
    }

    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        jsonrpc.ServeConn(conn)
    }
}

以前はこのコードに対してinvalid error <nil>が返ってしまっていました。

原因として、mapまたはsliceを戻り値の型として使用する場合に、server.readRequest で返すreflect.Valuenilで返っていたため、このCLによって空の値を作成して返すようにすることで解決されました。

Commit Message

net/rpc: Create empty maps and slices as return type
When a map or slice is used as a return type create an empty value
rather than a nil value.

Fixes #19588

Change-Id: I577fd74956172329745d614ac37d4db8f737efb8
Reviewed-on: https://go-review.googlesource.com/38474
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>

os: parse command line without shell32.dll

#15588に対する修正です。

Goではコマンドラインのパラメータをパースするのにshell32.dll からCommandLineToArgVを使用していますがshell32.dllは読み込みが遅いため、Windows向けにshell32.dllの使用を避けたコマンドライン解析を実装し、Goプログラムの起動を速くしています。

ベンチマークは以下の通りです

on my Windows 7 amd64:

name old time/op new time/op delta
RunningGoProgram-2 11.2ms ± 1% 10.4ms ± 2% -6.63% (p=0.000 n=9+10)

on my Windows XP 386:

name old time/op new time/op delta
RunningGoProgram-2 19.0ms ± 3% 12.1ms ± 1% -36.20% (p=0.000 n=10+10)

on @egonelbre Windows 10 amd64:

name old time/op new time/op delta
RunningGoProgram-8 17.0ms ± 1% 15.3ms ± 2% -9.71% (p=0.000 n=10+10)

Commit Message

os: parse command line without shell32.dll
Go uses CommandLineToArgV from shell32.dll to parse command
line parameters. But shell32.dll is slow to load. Implement
Windows command line parsing in Go. This should make starting
Go programs faster.

I can see these speed ups for runtime.BenchmarkRunningGoProgram

on my Windows 7 amd64:
name                old time/op  new time/op  delta
RunningGoProgram-2  11.2ms ± 1%  10.4ms ± 2%  -6.63%  (p=0.000 n=9+10)

on my Windows XP 386:=,.
name                old time/op  new time/op  delta
RunningGoProgram-2  19.0ms ± 3%  12.1ms ± 1%  -36.20%  (p=0.000 n=10+10)

on @egonelbre Windows 10 amd64:
name                old time/op  new time/op  delta
RunningGoProgram-8  17.0ms ± 1%  15.3ms ± 2%  -9.71%  (p=0.000 n=10+10)

This CL is based on CL 22932 by John Starks.

Fixes #15588.

Change-Id: Ib14be0206544d0d4492ca1f0d91fac968be52241
Reviewed-on: https://go-review.googlesource.com/37915
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

net/http: strip port from host in mux Handler

#10463に関連するCLです。

handlerとのマッチングを試みる前にリクエストのhostportが含まれているかをチェックし、含まれている場合にはmux.Handlerportを削除してpathをクリーンアップします。  CONNECTリクエストに関しては、pathhostは変更されずに使用されます。

Commit Message

net/http: strip port from host in mux Handler

This change strips the port in mux.Handler before attempting to
match handlers and adds a test for a request with port.

CONNECT requests continue to use the original path and port.

Fixes #10463

Change-Id: Iff3a2ca2b7f1d884eca05a7262ad6b7dffbcc30f
Reviewed-on: https://go-review.googlesource.com/38194
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

cmd/compile: disable typPtr caching in the backend

#15756に関連する変更です。 このIssueではビルドの高速化に向けて並行コンパイルを行うように多くの変更がされています。今回のCLはその中の一つです。(josharianが他にも多くのCLを出していますが、多すぎで全てを扱うことができませんでした。詳しくはIssueのほうをご覧になってください)

通常、* Tを生成するときには、結果のTypeをTにキャッシュして後で再作成しないようにしますが、そのキャッシュが concurrency-safeではありませんでした。その対応としてこのCLでは mutex を用いるのではなく、処理を開始する前にキャッシングを無効にすることで低コストでのconcurrency-safeを実現しています。 また、一般的に使用される* Tsをあらかじめ作成しておくと、新たに* Tsを生成するコストがあまりないため、パフォーマンスの悪化を一層防ぐことができます。

ベンチマークは以下の通りです。

name old alloc/op new alloc/op delta
Template 40.3MB ± 0% 40.4MB ± 0% +0.18% (p=0.001 n=10+10)
Unicode 29.8MB ± 0% 29.8MB ± 0% +0.11% (p=0.043 n=10+9)
GoTypes 114MB ± 0% 115MB ± 0% +0.33% (p=0.000 n=9+10)
SSA 855MB ± 0% 859MB ± 0% +0.40% (p=0.000 n=10+10)
Flate 25.7MB ± 0% 25.8MB ± 0% +0.35% (p=0.000 n=10+10)
GoParser 31.9MB ± 0% 32.1MB ± 0% +0.58% (p=0.000 n=10+10)
Reflect 79.6MB ± 0% 79.9MB ± 0% +0.31% (p=0.000 n=10+10)
Tar 26.9MB ± 0% 26.9MB ± 0% +0.21% (p=0.000 n=10+10)
XML 42.5MB ± 0% 42.7MB ± 0% +0.52% (p=0.000 n=10+9)
name old allocs/op new allocs/op delta
Template 394k ± 1% 393k ± 0% ~ (p=0.529 n=10+10)
Unicode 319k ± 1% 319k ± 0% ~ (p=0.720 n=10+9)
GoTypes 1.15M ± 0% 1.15M ± 0% +0.14% (p=0.035 n=10+10)
SSA 7.53M ± 0% 7.56M ± 0% +0.45% (p=0.000 n=9+10)
Flate 238k ± 0% 238k ± 1% ~ (p=0.579 n=10+10)
GoParser 318k ± 1% 320k ± 1% +0.64% (p=0.001 n=10+10)
Reflect 1.00M ± 0% 1.00M ± 0% ~ (p=0.393 n=10+10)
Tar 254k ± 0% 254k ± 1% ~ (p=0.075 n=10+10)
XML 395k ± 0% 397k ± 0% +0.44% (p=0.001 n=10+9)

Commit Message

cmd/compile: disable typPtr caching in the backend
The only new Types that the backend introduces
are pointers to Types generated by the frontend.
Usually, when we generate a *T,
we cache the resulting Type in T,
to avoid recreating it later.
However, that caching is not concurrency safe.
Rather than add mutexes, this CL disables that
caching before starting the backend.
The backend generates few enough new *Ts that the
performance impact of this is small, particularly
if we pre-create some commonly used *Ts.

Updates #15756

name       old alloc/op    new alloc/op    delta
Template      40.3MB ± 0%     40.4MB ± 0%  +0.18%  (p=0.001 n=10+10)
Unicode       29.8MB ± 0%     29.8MB ± 0%  +0.11%  (p=0.043 n=10+9)
GoTypes        114MB ± 0%      115MB ± 0%  +0.33%  (p=0.000 n=9+10)
SSA            855MB ± 0%      859MB ± 0%  +0.40%  (p=0.000 n=10+10)
Flate         25.7MB ± 0%     25.8MB ± 0%  +0.35%  (p=0.000 n=10+10)
GoParser      31.9MB ± 0%     32.1MB ± 0%  +0.58%  (p=0.000 n=10+10)
Reflect       79.6MB ± 0%     79.9MB ± 0%  +0.31%  (p=0.000 n=10+10)
Tar           26.9MB ± 0%     26.9MB ± 0%  +0.21%  (p=0.000 n=10+10)
XML           42.5MB ± 0%     42.7MB ± 0%  +0.52%  (p=0.000 n=10+9)

name       old allocs/op   new allocs/op   delta
Template        394k ± 1%       393k ± 0%    ~     (p=0.529 n=10+10)
Unicode         319k ± 1%       319k ± 0%    ~     (p=0.720 n=10+9)
GoTypes        1.15M ± 0%      1.15M ± 0%  +0.14%  (p=0.035 n=10+10)
SSA            7.53M ± 0%      7.56M ± 0%  +0.45%  (p=0.000 n=9+10)
Flate           238k ± 0%       238k ± 1%    ~     (p=0.579 n=10+10)
GoParser        318k ± 1%       320k ± 1%  +0.64%  (p=0.001 n=10+10)
Reflect        1.00M ± 0%      1.00M ± 0%    ~     (p=0.393 n=10+10)
Tar             254k ± 0%       254k ± 1%    ~     (p=0.075 n=10+10)
XML             395k ± 0%       397k ± 0%  +0.44%  (p=0.001 n=10+9)

Change-Id: I6c031ed4f39108f26969c5712b73aa2fc08cd10a
Reviewed-on: https://go-review.googlesource.com/38417
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>

runtime: introduce a type for lfstacks

リファクタのためにlfstacks型を導入しています。 lfstackはロックフリーなstackの先頭を表すuint64であり、lfstack のゼロ値は空のリストです。nodeは最初のフィールドとしてlfnodeを埋め込む必要があります。stackはGC可視のポインタをnodeに保持しないので、呼び出し元はnodeGCされないようにする必要があります。(通常は手動で管理されるメモリから割り当てます) pushpop、およびemptyのメソッドを持つlfstack型を作成することで、CLで書かれているコードのようなGoらしいコードを書くことができます。

Commit Message

runtime: introduce a type for lfstacks
The lfstack API is still a C-style API: lfstacks all have unhelpful
type uint64 and the APIs are package-level functions. Make the code
more readable and Go-style by creating an lfstack type with methods
for push, pop, and empty.

Change-Id: I64685fa3be0e82ae2d1a782a452a50974440a827
Reviewed-on: https://go-review.googlesource.com/38290
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>

runtime: disallow malloc or panic in scavenge

scavenge でのMallocsとパニックはmheap.lock 上でセルフデッドロックする可能性を孕んでいます。そのため、このCLではヒープロック状態でのmallocpanicを禁止しています。

Commit Message

runtime: disallow malloc or panic in scavenge
Mallocs and panics in the scavenge path are particularly nasty because
they're likely to silently self-deadlock on the mheap.lock. Avoid
sinking lots of time into debugging these issues in the future by
turning these into immediate throws.

Change-Id: Ib36fdda33bc90b21c32432b03561630c1f3c69bc
Reviewed-on: https://go-review.googlesource.com/38293
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>

cmd/compile/internal/syntax: add position info for { and } braces

{}括弧の位置情報を追加しています。 syntax.Nodesのメモリ使用量が約1.9%増加しますが、コンパイラ全体のメモリ使用量から見ると無視できる程度です。

Commit Message

cmd/compile/internal/syntax: add position info for { and } braces
This change adds position information for { and } braces in the
source. There's a 1.9% increase in memory use for syntax.Nodes,
which is negligible relative to overall compiler memory consumption.

Parsing the std library (using syntax package only) and memory
consumption before this change (fastest of 5 runs):

  $ go test -run StdLib -fast
  parsed 1516827 lines (3392 files) in 780.612335ms (1943124 lines/s)
  allocated 379.903Mb (486.673Mb/s)

After this change (fastest of 5 runs):

  $ go test -run StdLib -fast
  parsed 1517022 lines (3394 files) in 793.487886ms (1911840 lines/s)
  allocated 387.086Mb (267B/line, 487.828Mb/s)

While not an exact apples-to-apples comparison (the syntax package
has changed and is also parsed), the overall impact is small.

Also: Small improvements to nodes_test.go.

Change-Id: Ib8a7f90bbe79de33d83684e33b1bf8dbc32e644a
Reviewed-on: https://go-review.googlesource.com/38435
Reviewed-by: Matthew Dempsky <mdempsky@google.com>

strconv: optimize decimal ints formatting with smallsString

#19445に関する変更です。

smallsStringを使用して10進整数をフォーマットするように変更されています。 以前のCLで1~99の小さなdecimal intsについてキャッシュを用いて高速化を図っていたものの続きです。

GOARCH = amd64でのベンチマーク結果は以下の通りです。

name old time/op new time/op delta
FormatInt-4 2.51µs ± 2% 2.40µs ± 2% -4.51% (p=0.000 n=9+10)
AppendInt-4 1.67µs ± 2% 1.61µs ± 3% -3.74% (p=0.000 n=9+9)
FormatUint-4 698ns ± 2% 643ns ± 3% -7.95% (p=0.000 n=10+8)
AppendUint-4 478ns ± 1% 418ns ± 2% -12.61% (p=0.000 n=8+10)
AppendUintVarlen/1-4 9.30ns ± 6% 9.15ns ± 1% ~ (p=0.199 n=9+10)
AppendUintVarlen/12-4 9.12ns ± 0% 9.16ns ± 2% ~ (p=0.307 n=9+9)
AppendUintVarlen/123-4 18.6ns ± 2% 18.7ns ± 0% ~ (p=0.091 n=10+6)
AppendUintVarlen/1234-4 19.1ns ± 4% 17.7ns ± 1% -7.35% (p=0.000 n=10+9)
AppendUintVarlen/12345-4 21.5ns ± 3% 20.7ns ± 3% -3.78% (p=0.002 n=9+10)
AppendUintVarlen/123456-4 23.5ns ± 3% 20.9ns ± 1% -11.14% (p=0.000 n=10+9)
AppendUintVarlen/1234567-4 25.0ns ± 2% 23.6ns ± 7% -5.48% (p=0.004 n=9+10)
AppendUintVarlen/12345678-4 26.8ns ± 2% 23.4ns ± 2% -12.79% (p=0.000 n=9+10)
AppendUintVarlen/123456789-4 29.8ns ± 3% 26.5ns ± 5% -11.03% (p=0.000 n=10+10)
AppendUintVarlen/1234567890-4 31.6ns ± 3% 26.9ns ± 3% -14.95% (p=0.000 n=10+9)
AppendUintVarlen/12345678901-4 33.8ns ± 3% 29.3ns ± 5% -13.21% (p=0.000 n=10+10)
AppendUintVarlen/123456789012-4 35.5ns ± 4% 29.2ns ± 4% -17.82% (p=0.000 n=10+10)
AppendUintVarlen/1234567890123-4 37.6ns ± 4% 31.4ns ± 3% -16.48% (p=0.000 n=10+10)
AppendUintVarlen/12345678901234-4 39.8ns ± 6% 32.0ns ± 7% -19.60% (p=0.000 n=10+10)
AppendUintVarlen/123456789012345-4 40.7ns ± 0% 34.4ns ± 4% -15.55% (p=0.000 n=6+10)
AppendUintVarlen/1234567890123456-4 45.4ns ± 6% 35.1ns ± 4% -22.66% (p=0.000 n=10+10)
AppendUintVarlen/12345678901234567-4 45.1ns ± 1% 36.7ns ± 4% -18.77% (p=0.000 n=9+10)
AppendUintVarlen/123456789012345678-4 46.9ns ± 0% 36.4ns ± 3% -22.49% (p=0.000 n=9+10)
AppendUintVarlen/1234567890123456789-4 50.6ns ± 6% 38.8ns ± 3% -23.28% (p=0.000 n=10+10)
AppendUintVarlen/12345678901234567890-4 51.3ns ± 2% 38.4ns ± 0% -25.00% (p=0.000 n=9+8)

Commit Message

strconv: optimize decimal ints formatting with smallsString

Benchmark results for GOARCH=amd64:

name                                     old time/op  new time/op  delta
FormatInt-4                              2.51µs ± 2%  2.40µs ± 2%   -4.51%  (p=0.000 n=9+10)
AppendInt-4                              1.67µs ± 2%  1.61µs ± 3%   -3.74%  (p=0.000 n=9+9)
FormatUint-4                              698ns ± 2%   643ns ± 3%   -7.95%  (p=0.000 n=10+8)
AppendUint-4                              478ns ± 1%   418ns ± 2%  -12.61%  (p=0.000 n=8+10)
AppendUintVarlen/1-4                     9.30ns ± 6%  9.15ns ± 1%     ~     (p=0.199 n=9+10)
AppendUintVarlen/12-4                    9.12ns ± 0%  9.16ns ± 2%     ~     (p=0.307 n=9+9)
AppendUintVarlen/123-4                   18.6ns ± 2%  18.7ns ± 0%     ~     (p=0.091 n=10+6)
AppendUintVarlen/1234-4                  19.1ns ± 4%  17.7ns ± 1%   -7.35%  (p=0.000 n=10+9)
AppendUintVarlen/12345-4                 21.5ns ± 3%  20.7ns ± 3%   -3.78%  (p=0.002 n=9+10)
AppendUintVarlen/123456-4                23.5ns ± 3%  20.9ns ± 1%  -11.14%  (p=0.000 n=10+9)
AppendUintVarlen/1234567-4               25.0ns ± 2%  23.6ns ± 7%   -5.48%  (p=0.004 n=9+10)
AppendUintVarlen/12345678-4              26.8ns ± 2%  23.4ns ± 2%  -12.79%  (p=0.000 n=9+10)
AppendUintVarlen/123456789-4             29.8ns ± 3%  26.5ns ± 5%  -11.03%  (p=0.000 n=10+10)
AppendUintVarlen/1234567890-4            31.6ns ± 3%  26.9ns ± 3%  -14.95%  (p=0.000 n=10+9)
AppendUintVarlen/12345678901-4           33.8ns ± 3%  29.3ns ± 5%  -13.21%  (p=0.000 n=10+10)
AppendUintVarlen/123456789012-4          35.5ns ± 4%  29.2ns ± 4%  -17.82%  (p=0.000 n=10+10)
AppendUintVarlen/1234567890123-4         37.6ns ± 4%  31.4ns ± 3%  -16.48%  (p=0.000 n=10+10)
AppendUintVarlen/12345678901234-4        39.8ns ± 6%  32.0ns ± 7%  -19.60%  (p=0.000 n=10+10)
AppendUintVarlen/123456789012345-4       40.7ns ± 0%  34.4ns ± 4%  -15.55%  (p=0.000 n=6+10)
AppendUintVarlen/1234567890123456-4      45.4ns ± 6%  35.1ns ± 4%  -22.66%  (p=0.000 n=10+10)
AppendUintVarlen/12345678901234567-4     45.1ns ± 1%  36.7ns ± 4%  -18.77%  (p=0.000 n=9+10)

regexp: reduce allocs in regexp.Match for onepass regex

#19573に対するCLです。(はやぶささん!!)

onepass正規表現に対するregexp.Match内のアロケーションを削減しています。 ベンチマークを見てもわかる通りかなりの速度改善になっています。

regexp.Matchのncap = 0としてあるため、onepassでない正規表現においてはregexp.Matchの割り当てはありませんが、onepass正規表現の場合、ncap = 0であってもm.matchcapの長さはそのままであるため無駄なアロケーションが発生してしまっていました。

benchmark old ns/op new ns/op delta
BenchmarkMatch_onepass_regex/32-4 6465 4628 -28.41%
BenchmarkMatch_onepass_regex/1K-4 208324 151558 -27.25%
BenchmarkMatch_onepass_regex/32K-4 7230259 5834492 -19.30%
BenchmarkMatch_onepass_regex/1M-4 234379810 166310682 -29.04%
BenchmarkMatch_onepass_regex/32M-4 7903529363 4981119950 -36.98%
benchmark old MB/s new MB/s speedup
BenchmarkMatch_onepass_regex/32-4 4.95 6.91 1.40x
BenchmarkMatch_onepass_regex/1K-4 4.92 6.76 1.37x
BenchmarkMatch_onepass_regex/32K-4 4.53 5.62 1.24x
BenchmarkMatch_onepass_regex/1M-4 4.47 6.30 1.41x
BenchmarkMatch_onepass_regex/32M-4 4.25 6.74 1.59x

Commit Message

regexp: reduce allocs in regexp.Match for onepass regex
There were no allocations in regexp.Match for *non* onepass regex
because m.matchcap length is reset to zero (ncap=0 for regexp.Match).

But, as for onepass regex, m.matchcap length remains as it is even when
ncap=0 and it leads needless allocations.

benchmark                                    old ns/op      new ns/op      delta
BenchmarkMatch_onepass_regex/32-4      6465           4628           -28.41%
BenchmarkMatch_onepass_regex/1K-4      208324         151558         -27.25%
BenchmarkMatch_onepass_regex/32K-4     7230259        5834492        -19.30%
BenchmarkMatch_onepass_regex/1M-4      234379810      166310682      -29.04%
BenchmarkMatch_onepass_regex/32M-4     7903529363     4981119950     -36.98%

benchmark                                    old MB/s     new MB/s     speedup
BenchmarkMatch_onepass_regex/32-4      4.95         6.91         1.40x
BenchmarkMatch_onepass_regex/1K-4      4.92         6.76         1.37x
BenchmarkMatch_onepass_regex/32K-4     4.53         5.62         1.24x
BenchmarkMatch_onepass_regex/1M-4      4.47         6.30         1.41x
BenchmarkMatch_onepass_regex/32M-4     4.25         6.74         1.59x

benchmark                                    old allocs     new allocs     delta
BenchmarkMatch_onepass_regex/32-4      32             0              -100.00%
BenchmarkMatch_onepass_regex/1K-4      1024           0              -100.00%
BenchmarkMatch_onepass_regex/32K-4     32768          0              -100.00%
BenchmarkMatch_onepass_regex/1M-4      1048576        0              -100.00%
BenchmarkMatch_onepass_regex/32M-4     104559255      0              -100.00%

benchmark                                    old bytes      new bytes     delta
BenchmarkMatch_onepass_regex/32-4      512            0             -100.00%
BenchmarkMatch_onepass_regex/1K-4      16384          0             -100.00%
BenchmarkMatch_onepass_regex/32K-4     524288         0             -100.00%
BenchmarkMatch_onepass_regex/1M-4      16777216       0             -100.00%
BenchmarkMatch_onepass_regex/32M-4     2019458128     0             -100.00%

Fixes #19573

Change-Id: I033982d0003ebb0360bb40b92eb3941c781ec74d
Reviewed-on: https://go-review.googlesource.com/38270
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>

Go commit 流し読み(2017/3/12)

Goでその日Mergeされたもののうち、ある程度大きいものを取り上げていきます。 tenntennさんの影響でGoにcontributeして以降せっかく毎日commitを追っているから、ということでy_yagiさんをリスペクトして始めましたが、毎日続けるのはつらそうなので気の向いたときに記事にしようと思います。(要望があればがんばるかもしれません)

cmd/vet: eliminate “might be too small for shift” warnings

Issueに対する修正です。

func f(i int) int {
  return i >> 32
}

以前までは上記のコードにあるようなマシン依存型である int , uint , uintptr に対して、vetが might be too small for shift という警告を出してしまっていました。 この変更によって、GOARCH 環境変数が設定されている場合はそこから int ,uint ,uintptr のビットサイズを決定し、それ以外の場合にはホスト固有のサイズを使用するようになりました。

strconv: fix performance regression in integer formatting on 32bit platforms

32ビットプラットフォームでの整数フォーマットのパフォーマンス改善をしています。この変更は64bitプラットフォームにはほとんど影響がありません。 strconv: use % instead of computing the remainder from the quotientに関連したCLです。 上記のCLでは、1つのDIV命令の場合にコンパイラ/ を順番に認識すると想定していました。 しかし、別々のランタイムの関数を使用して除算とモジュロを計算している32bitプラットフォーム上の64bitオペランドでは順番に認識されるとは限りません。 そのため、上記のCLで消されてしまった32bitプラットフォームで有益な最適化を復元しています。

ベンチマークは以下の通りです。

386

name old time/op new time/op delta
FormatInt-2 6.06µs ± 0% 6.02µs ± 0% -0.70% | (p=0.000 n=20+20)
AppendInt-2 4.98µs ± 0% 4.98µs ± 0% ~ |(p=0.747 n=18+18)
FormatUint-2 1.93µs ± 0% 1.85µs ± 0% -4.19%| (p=0.000 n=20+20)
AppendUint-2 1.71µs ± 0% 1.64µs ± 0% -3.68%| (p=0.000 n=20+20)

amd64

name old time/op new time/op delta
FormatInt-2 2.41µs ± 0% 2.41µs ± 0% -0.09% | (p=0.010 n=18+18)
AppendInt-2 1.77µs ± 0% 1.77µs ± 0% +0.08% | (p=0.000 n=18+18)
FormatUint-2 653ns ± 1% 653ns ± 0% ~ |(p=0.178 n=20+20)
AppendUint-2 514ns ± 0% 513ns ± 0% -0.13% | (p=0.000 n=20+17)

無償の愛は果たして存在するのか?〜少し学問的に考えてみた〜

はじめに

大学の友達と食堂で話していたら恋愛の話しになり、気づいたら「無償の愛が存在するのかどうか」という話になっていた。 20分程度の議論ではあったが、そのまま忘れ去ってしまうのももったいないので備忘録程度に残しておく。

この議論には絶対的結論があるわけではないので、反論などあればぜひ(建設的な)議論をしたいです。

無条件に人を愛するとは

f:id:niconegoto:20170112223915j:plain 今回は無償の愛を「相手の存在全てを愛すること」「一切見返りを求めずに愛すること」として、様々な観点で無償の愛について見ていった。

アインシュタイン

突然だが、僕がアインシュタインを愛していたとする。 「アインシュタイン」という文字列は名前なのだが、この「アインシュタイン」という固有名詞は

などといったその人を表す言葉の集まりと同等であると考えられていた。 ただ、この「男性」という特徴が「女性」になったからといって「アインシュタイン」は「アインシュタイン」たる存在であることには変わりない。 よって、「アインシュタイン」を「アインシュタイン」たらしめる独自性のようなものは、それらの特徴の否定の契機を孕んでいるといえる。 つまり「アインシュタイン」という存在はは彼の持つ特徴を表現している一方で、むしろその否定の契機をも含むものである。

そのため、僕が「アインシュタイン」を無条件に愛していると言った時、それは彼が女性であろうと、天才物理学者でなかろうと、その存在を愛していなければならない。 それが人を愛すると言うことである。

僕にはとうていそんなことはできそうにない。

贈与論

次に、贈与論の立場から話しをすることになった。 愛も「愛してるよ。」「私も愛してるわ。」という贈与の関係だと言える。

無条件の愛というものは見返りを求めない愛、とも言い換えられるので、もし見返りを求めて「愛しているよ。」と言っているのであればそれは無償の愛とは言えない。 ただ、残念なことに贈与の関係において、最初の一方的な贈与は存在しないとする考えがある。 どういうことかというと、どちらかが最初に告白をして「愛してるよ。」と言い始めたとしても、それは見返りを求めない贈与ではないということである。なぜなら、告白以前に相手が「自分に気があるかも」と思わせるような行動をして気付かぬうちに贈与をしていたり、自分の中に相手がつきあいたいなと思えるような魅力を持っていてそれを見返りとして期待していたりするからである。つまり、告白という行為は少なからず以前に贈与を得ていたり、その後の贈与を期待するものなのである。

マザーテレサ

無償の愛、といえばこのマザーテレサを思い浮かべる方も多いだろう。 wikipediaにも

テレサは授賞式の際にも特別な正装はせず、普段と同じく白い木綿のサリーと皮製のサンダルという粗末な身なりで出席した。賞金19万2000ドルは全てカルカッタの貧しい人々のために使われることになった上、授賞式の場においては、「私のための晩餐会は不要です。その費用はどうか貧しい人々のためにお使い下さい」とも要望した。賞金を受け取った時、「このお金でいくつのパンが買えますか」と言ったという。

と書いてあり、ノーベル賞にも選ばれていることからも一見無償の愛を体現しているかに思える。 ただ、残念なことにテレサの行動の背景には宗教的「神」の存在があり、カトリック信者であるため、神のため、という意図が働いている。そのため完全な「無償の愛」であるとは言いがたいのである。

そのほか、親の愛も無償の愛にちかいのではないかという声もあったが、親も「子供をしっかり育てている親である」という社会的ラベルの為の愛ではないかという結論に達し、無償の愛ではないという判断となった。(実際僕も親に「自分の子供自慢の為に教育費をかけてあげている」「その分良い大学、良い会社にいってくれ」といった事を言われたことがある。正直な親である。)

結論、結局人間は 「愛している自分を愛するため」か「愛されているので愛す」という形でしか愛することができないのではないか という結論に達した。

まあこんな感じで我々は「この世界に無償の愛は存在しない」という悲しい結論に達し、とぼとぼと試験勉強に向かったのであった。

(10分くらいで書いたので誤植や論理の飛躍があるかもしれませんがその辺りはご了承ください。)

自分のQiita記事まとめ

今後は主にはてブを使用するつもりのため、メモに載せておく。

qiita.com

qiita.com

qiita.com