Skip to content

Commit

Permalink
all: add pdfgen and generated PDFs
Browse files Browse the repository at this point in the history
  • Loading branch information
changkun committed May 1, 2022
1 parent a134537 commit 67bd651
Show file tree
Hide file tree
Showing 17 changed files with 389 additions and 93 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ all:
hugo
s:
hugo server -D

pdf:
go build pdfgen.go
Binary file added content/bench-time.pdf
Binary file not shown.
Binary file added content/cgo-handle.pdf
Binary file not shown.
Binary file added content/generic-options.pdf
Binary file not shown.
Binary file added content/pointer-params.pdf
Binary file not shown.
27 changes: 13 additions & 14 deletions content/posts/bench-time.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
date: 2020-09-30T09:02:20+01:00
toc: true
slug: /bench-time
tags:
- Benchmark
Expand All @@ -9,14 +8,14 @@ tags:
title: Eliminating A Source of Measurement Errors in Benchmarks
---

Author(s): [Changkun Ou](https://changkun.de)
Author(s): [Changkun Ou](mailto:research[at]changkun.de)

Permalink: https://golang.design/research/bench-time

About six months ago, I did a [presentation](https://golang.design/s/gobench)
that talks about how to conduct a reliable benchmark in Go.
Recently, I submitted an issue [#41641](https://golang.org/issue/41641) to the Go project, which is also a subtle issue that you might need to address in some cases.

<!--abstract-->
About six months ago, I did a presentation[^ou2020bench]
that talks about how to conduct a reliable benchmark[^beyer2019reliable] in Go.
Recently, I submitted an issue #41641[^ou2020timer] to the Go project, which is also a subtle issue that you might need to address in some cases.
<!--more-->

## Introduction
Expand Down Expand Up @@ -182,7 +181,7 @@ func (b *B) StopTimer() {
}
```

As we know that `runtime.ReadMemStats` stops the world, and each call to it is very time-consuming. This is an known issue [#20875](https://golang.org/issue/20875) regarding `runtime.ReadMemStats` in benchmarking.
As we know that `runtime.ReadMemStats` stops the world, and each call to it is very time-consuming. This is an known issue #20875[^snyder2020memstats] regarding `runtime.ReadMemStats` in benchmarking.

Since we do not care about memory allocation at the moment, to avoid this issue, let's just hacking the source code by just comment out the call to `runtime.ReadMemStats`:

Expand Down Expand Up @@ -295,7 +294,7 @@ to the execution time of target code plus the overhead of calling `now()`:
![](../assets/bench-time/flow.png)

Assume the target code consumes in `T` ns, and the overhead of `now()` is `t` ns.
Now, let's run the target code `N` times.
Now, let's run the target code `N` times.
The total measured time is `T*N+t`, then the average of a single iteration
of the target code is `T+t/N`. Thus, the systematic measurement error becomes: `t/N`.
Therefore with a higher `N`, you can get rid of the systematic error.
Expand Down Expand Up @@ -357,11 +356,11 @@ calibrate := func(d time.Duration, n int) time.Duration {
fmt.Printf("%v ns/op\n", calibrate(r.T, r.N))
```

As a take-away message, if you would like to write a micro-benchmark (whose runs in nanoseconds), and you have to interrupt the timer to clean up and reset some resources for some reason, then you must do a calibration on the measurement. If the Go's benchmark facility plans to fix #41641, then it is great; but if they don't, at least you are aware of this issue and know how to fix it now.
As a take-away message, if you would like to write a micro-benchmark (whose runs in nanoseconds), and you have to interrupt the timer to clean up and reset some resources for some reason, then you must do a calibration on the measurement. If the Go's benchmark facility plans to fix #41641[^ou2020timer], then it is great; but if they don't, at least you are aware of this issue and know how to fix it now.

## Further Reading Suggestions
## References

- Changkun Ou. Conduct Reliable Benchmarking in Go. March 26, 2020. https://golang.design/s/gobench
- Changkun Ou. testing: inconsistent benchmark measurements when interrupts timer. Sep 26, 2020. https://golang.org/issue/41641
- Josh Bleecher Snyder. testing: consider calling ReadMemStats less during benchmarking. Jul 1, 2017. https://golang.org/issue/20875
- Beyer, D., Löwe, S. & Wendler, P. Reliable benchmarking: requirements and solutions. Int J Softw Tools Technol Transfer 21, 1–29 (2019). https://doi.org/10.1007/s10009-017-0469-y
[^ou2020bench]: Changkun Ou. 2020. Conduct Reliable Benchmarking in Go. TalkGo Meetup. Virtual Event. March 26. https://golang.design/s/gobench
[^ou2020timer]: Changkun Ou. 2020. testing: inconsistent benchmark measurements when interrupts timer. The Go Project Issue Tracker. Sep 26. https://go.dev/issue/41641
[^snyder2020memstats]: Josh Bleecher Snyder. 2020. testing: consider calling ReadMemStats less during benchmarking. The Go Project Issue Tracker. Jul 1. https://go.dev/issue/20875
[^beyer2019reliable]: Beyer, D., Löwe, S. \& Wendler, P. 2019. Reliable benchmarking: requirements and solutions. International Journal on Software Tools for Technology Transfer. Issue 21. https://doi.org/10.1007/s10009-017-0469-y
44 changes: 21 additions & 23 deletions content/posts/cgo-handle.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
date: 2021-06-10T19:24:41+02:00
toc: true
slug: /cgo-handle
tags:
- Go
Expand All @@ -11,17 +10,17 @@ tags:
title: A Concurrent-safe Centralized Pointer Managing Facility
---

Author: [Changkun Ou](https://changkun.de)
Author(s): [Changkun Ou](mailto:research[at]changkun.de)

Permalink: [https://golang.design/research/cgo-handle](https://golang.design/research/cgo-handle)

<!--abstract-->
In the Go 1.17 release, we contributed a new cgo facility [runtime/cgo.Handle](https://tip.golang.org/pkg/runtime/cgo/#Handle) in order to help future cgo applications better and easier to build concurrent-safe applications while passing pointers between Go and C. This article will guide us through the feature by asking what the feature offers to us, why we need such a facility, and how exactly we contributed to the implementation eventually.

<!--more-->

## Starting from Cgo and X Window Clipboard

Cgo is the de facto approach to interact with the C facility in Go. Nevertheless, how often do we need to interact with C in Go? The answer to the question depends on how much we work on the system level or how often do we have to utilize a legacy C library, such as for image processing. Whenever a Go application needs to use a legacy from C, it needs to import a sort of C dedicated package as follows, then on the Go side, one can simply call the `myprint` function through the imported `C` symbol:
Cgo[^go2019cgo] is the de facto approach to interact with the C facility in Go. Nevertheless, how often do we need to interact with C in Go? The answer to the question depends on how much we work on the system level or how often do we have to utilize a legacy C library, such as for image processing. Whenever a Go application needs to use a legacy from C, it needs to import a sort of C dedicated package as follows, then on the Go side, one can simply call the `myprint` function through the imported `C` symbol:

```go
/*
Expand All @@ -40,7 +39,7 @@ func main() {
}
```

A few months ago, while we were working on building a new package [`golang.design/x/clipboard`](https://golang.design/x/clipboard), a package that offers cross-platform clipboard access. We found out, there is a lacking of facility in Go, despite the variety of approaches in the wild, still suffering from soundness and performance issues.
A few months ago, while we were working on building a new package [`golang.design/x/clipboard`](https://golang.design/x/clipboard)[^ou2021clipboard], a package that offers cross-platform clipboard access. We found out, there is a lacking of facility in Go, despite the variety of approaches in the wild, still suffering from soundness and performance issues.

In the [`golang.design/x/clipboard`](https://golang.design/x/clipboard) package, we had to cooperate with cgo to access system level APIs (technically, it is an API from a legacy and widely used C system), but lacking the facility of knowing the execution progress on the C side. For instance, on the Go side, we have to call the C code in a goroutine, then do something else in parallel:

Expand Down Expand Up @@ -69,7 +68,7 @@ clipboard.Write("some information")
We have to guarantee from its inside that when the function returns,
the information should be available to be accessed by other applications.

Back then, our first idea to deal with the problem was to pass a channel from Go to C, then send a value through the channel from C to Go. After a quick research, we realized that it is impossible because channels cannot be passed as a value between C and Go due to the [rules of passing pointers in Cgo](https://pkg.go.dev/cmd/cgo#hdr-Passing_pointers) (see a previous [proposal document](https://golang.org/design/12416-cgo-pointers)). Even there is a way to pass the entire channel value to the C, there will be no facility to send values through that channel on the C side because C does not have the language support of the `<-` operator.
Back then, our first idea to deal with the problem was to pass a channel from Go to C, then send a value through the channel from C to Go. After a quick research, we realized that it is impossible because channels cannot be passed as a value between C and Go due to the [rules of passing pointers in Cgo](https://pkg.go.dev/cmd/cgo#hdr-Passing_pointers) (see a previous proposal document [^taylor2015cgorules] [^taylor2015cgorules2]). Even there is a way to pass the entire channel value to the C, there will be no facility to send values through that channel on the C side because C does not have the language support of the `<-` operator.

The next idea was to pass a function callback, then get it called on the C side. The function's execution will use the desired channel to send a notification back to the waiting goroutine.

Expand Down Expand Up @@ -142,11 +141,11 @@ func() {
}
```

Note that the `funcCallback` must be a global function variable. Otherwise, it is a violation of the [cgo pointer passing rules](https://pkg.go.dev/cmd/cgo/#hdr-Passing_pointers) as mentioned before.
Note that the `funcCallback` must be a global function variable. Otherwise, it is a violation of the [cgo pointer passing rules](https://pkg.go.dev/cmd/cgo/#hdr-Passing_pointers) as mentioned before.

Furthermore, an immediate reaction to the readability of the above code is: too complicated. The demonstrated approach can only assign one function at a time, which is also a violation of the concurrent nature. Any per-goroutine dedicated application will not benefit from this approach because they need a per-goroutine function callback instead of a single global callback. By then, we wonder if there is a better and elegant approach to deal with it.

Through our research, we found that the need occurs quite often in the community, and is also being proposed in [golang/go#37033](https://golang.org/issue/37033). Luckily, such a facility is now ready in Go 1.17 :)
Through our research, we found that the need occurs quite often in the community, and is also being proposed in golang/go#37033[^dubov2020cgohandle]. Luckily, such a facility is now ready in Go 1.17 :)

## What is `runtime/cgo.Handle`?

Expand Down Expand Up @@ -268,7 +267,7 @@ Next question: How to implement `cgo.Handle`?

## First Attempt

The first attempt was a lot complicated. Since we need a centralized way to manage all pointers in a concurrent-safe way, the quickest idea that comes to our mind was the `sync.Map` that maps a unique number to the desired value. Hence, we can easily use a global `sync.Map`:
The first attempt[^ou2021cgohandle] was a lot complicated. Since we need a centralized way to manage all pointers in a concurrent-safe way, the quickest idea that comes to our mind was the `sync.Map` that maps a unique number to the desired value. Hence, we can easily use a global `sync.Map`:

```go
package cgo
Expand Down Expand Up @@ -485,7 +484,7 @@ func (h Handle) Delete() {
}
```

In this implementation, we do not have to assume the runtime mechanism but just use the language. As long as the Go 1 compatibility keeps the promise `sync.Map` to work, there will be no need to rework the whole `Handle` design. Because of its simplicity, this is the accepted approach (see [CL 295369](https://golang.org/cl/295369)) by the Go team.
In this implementation, we do not have to assume the runtime mechanism but just use the language. As long as the Go 1 compatibility keeps the promise `sync.Map` to work, there will be no need to rework the whole `Handle` design. Because of its simplicity, this is the accepted approach (see CL 295369[^out2020cgohandle2]) by the Go team.

Aside from a future re-implementation of `sync.Map` that optimizes parallelism, the `Handle` will automatically benefit from it. Let us do a final benchmark that compares the previous method and the current approach:

Expand Down Expand Up @@ -521,14 +520,14 @@ Simpler, faster, why not?

## Conclusion

This article discussed the newly introduced `runtime/cgo.Handle` facility coming in the Go 1.17 release that we contributed. The `Handle` facility enables us to pass Go values between Go and C back and forth without breaking the cgo pointer passing rules. After a short introduction to the usage of the feature, we first discussed a first attempt implementation based on the fact that the runtime garbage collector is not a moving GC and the escape behavior of `interface{}` arguments.
This article discussed the newly introduced `runtime/cgo.Handle` facility coming in the Go 1.17 release that we contributed. The `Handle` facility enables us to pass Go values between Go and C back and forth without breaking the cgo pointer passing rules. After a short introduction to the usage of the feature, we first discussed a first attempt implementation based on the fact that the runtime garbage collector is not a moving GC and the escape behavior of `interface{}` arguments.
After a few discussions of the ambiguity of the Handle semantics and the drawbacks in the previous implementation, we also introduced a straightforward and better-performed approach and demonstrated its performance.

As a real-world demonstration, we have been using the mentioned two approaches
in two of our released packages for quite a long time:
[golang.design/x/clipboard](https://github.com/golang-design/clipboard)
and [golang.design/x/hotkey](https://github.com/golang-design/hotkey)
before in their `internal/cgo` package.
and [golang.design/x/hotkey](https://github.com/golang-design/hotkey) [^ou2021hotkey]
before in their `internal/cgo` package.
We are looking forward to switching to the officially released `runtime/cgo`
package in the Go 1.17 release.

Expand All @@ -543,14 +542,13 @@ When we allocate 100 handles per second, the handle space can run out in
[CC us](mailto:hi[at]golang.design) when you send a CL,
it would also be interesting for us to read your excellent approach._

## References

## Further Reading Suggestions

- Alex Dubov. runtime: provide centralized facility for managing (c)go pointer handles. Feb 5, 2020. https://golang.org/issue/37033
- Changkun Ou. runtime/cgo: add Handle for managing (c)go pointers Feb 21, 2021. https://golang.org/cl/294670
- Changkun Ou. runtime/cgo: add Handle for managing (c)go pointers Feb 23, 2021. https://golang.org/cl/295369
- Ian Lance Taylor. cmd/cgo: specify rules for passing pointers between Go and C. Aug 31, 2015. https://golang.org/issue/12416
- Ian Lance Taylor. Proposal: Rules for passing pointers between Go and C. October, 2015. https://golang.org/design/12416-cgo-pointers
- Go Contributors. cgo. Mar 12, 2019. https://github.com/golang/go/wiki/cgo
- The golang.design Initiative. 📋 cross-platform clipboard package in Go. Feb 25, 2021. https://github.com/golang-design/clipboard
- The golang.design Initiative. ⌨️ cross-platform hotkey package in GO. Feb 27, 2021. https://github.com/golang-design/hotkey
[^dubov2020cgohandle]: Alex Dubov. 2020. runtime: provide centralized facility for managing (c)go pointer handles. The Go Project Issue Tracker. Feb 5. https://go.dev/issue/37033
[^ou2021cgohandle]: Changkun Ou. 2021. runtime/cgo: add Handle for managing (c)go pointers. The Go Project CL Tracker. Feb 21, 2021. https://go.dev/cl/294670
[^out2020cgohandle2]: Changkun Ou. 2021. runtime/cgo: add Handle for managing (c)go pointers. The Go Project CL Tracker. Feb 23, 2021. https://go.dev/cl/295369
[^taylor2015cgorules]: Ian Lance Taylor. 2015. cmd/cgo: specify rules for passing pointers between Go and C. The Go Project Issue Tracker. Aug 31. https://go.dev/issue/12416
[^taylor2015cgorules2]: Ian Lance Taylor. 2015. Proposal: Rules for passing pointers between Go and C. The Go project design proposals. https://golang.org/design/12416-cgo-pointers
[^go2019cgo]: Go Contributors. cgo. Mar 12, 2019. https://github.com/golang/go/wiki/cgo
[^ou2021clipboard]: Changkun Ou. 2021. cross-platform clipboard package. The golang.design Initiative. Feb 25. https://github.com/golang-design/clipboard
[^ou2021hotkey]: Changkun Ou. 2021. cross-platform hotkey package. The golang.design Initiative. Feb 27. https://github.com/golang-design/hotkey
Loading

0 comments on commit 67bd651

Please sign in to comment.