Go Quick Clone "yes"사용
17121 단어 gnugotutorialperformance
yes
로 속도 실험을 하고 싶습니다.YES란 무엇인가?
만약 맥이나 linux를 사용한다면, 터미널로 이동해서
yes
를 입력하십시오.너는 반드시 무한류를 얻어야 한다y
.또는 yes yes
로 실행할 수도 있습니다. yes
의 무한 흐름을 얻을 수 있습니다.또는 yes no
.이 도구의 그래픽 표현:
이것은 무슨 의의가 있습니까?
만약 당신이 매우 긴 설치 스크립트를 가지고 있다면, 아무도 참여하지 않는 상황에서 실행하기를 원합니다.만약 스크립트에 모든 문제에 긍정적인 답안이 있도록 강제하는 표지가 있다면, 문제는 없다.하지만 시나리오가 매번 문제와 답안
y
이나 n
을 기다리게 한다면 어떻게 해야 하나요?만약 네가 개의치 않는다면, 네가 모든 질문에 대답할 줄 알기 때문이냐?그리고 너는 할 수 있다.$ yes | myInstallScript.sh
따라서 일정한 y
흐름이 파이프를 통해 설치 스크립트로 전송되기 때문에 어떤 질문을 받을 때마다 파이프가 해답을 해 줍니다.또 다른 용도는 가상 데이터를 생성해야 한다면 필요에 따라 파이프
y
를 파일로 전송할 수 있다는 것이다.그래서true인 경우 yes를 인쇄합니까?
약간, 하지만 전부는 아니다. 왜냐하면 우리는 성능에 관심을 가지기 때문이다.만약 우리가 개의치 않는다면, 우리는 할 수 있다.
expletive := os.Args[1]
if expletive == "" {
expletive = "y"
}
for {
fmt.Println(expletive)
}
이것이 바로 이야기의 결말이다.그러나 이 실현이 얼마나 빠른지 가늠해 봅시다.만약 맥을 사용하고 있다면,
yes
을 통해 계속 설치하십시오 pv
.linux에서, 당신은 그것을 가지고 있을 수도 있습니다.Windows에서는 Cygwin을 사용해야 할 수도 있습니다.Pv는 파이핑을 통해 데이터를 측정하는 도구입니다.실현 1: 무한 순환 인쇄
go build yes.go
./yes | pv > /dev/null
평균적으로 인쇄됩니다 [1.31MiB/s]맥(BSD 구현)에 설치된 리얼
brew install pv
이 대략[23.4MiB/s]인 것을 감안하면 우리의 첫 번째 실현은 좋지 않다는 결론이다.실현 2: 1억 장을 인쇄할 수 있는데 왜 한 장만 인쇄합니까
지금까지 우리는 줄곧 하고 있다
for {
fmt.Println(expletive)
}
이것은 단지 하나하나 인쇄 순환일 뿐이기 때문에 그다지 효과가 없는 것 같다.중복되는 문자열을 많이 인쇄하여 흥미를 더합니다.fmt.Println(strings.Repeat(expletive+"\n", 100))
이것은 우리에게 [89.8Mb/s]를 주었다.많이 좋아졌어!우리는 이미 맥 컴퓨터에 설치된 원시 버전 yes
의 기준 테스트를 통과했다.왜 100에 멈춰 있어요?
fmt.Println(strings.Repeat(expletive+"\n", 100000000))
이로 인해 [1.37GiB/s]우리는 이곳에서 뭔가를 찾은 것 같다.1억 번이 넘는 중복은 적어도 내 컴퓨터에서 두 가지 일이 발생할 것이다. 차이가 없거나, 전혀 작용하지 않거나, 쓸 수 없다.3: Goroutines 조립 실현!
사람들은 이곳에서 즐거움이 도움이 될 것이라고 생각할 수도 있다.만약
yes
여러 개의 goroutine에 고속으로 던져진다면, 이것은 높은 흡수량을 가져오지 않겠는가?나 몰라!저희가 발견할 거예요!문제는 우리가 단지 이렇게 했을 뿐이라면:
yesChan = make(chan string)
go func() {
yesChan <- strings.Repeat(expletive+"\n", 100000000)
}()
go func() {
fmt.Println(<-yesChan)
}()
우리는 몇 초 후에 흥미로운 문제에 직면하게 될 것이다.panic: too many concurrent operations on a single file or socket
이것은 말하지 않아도 알 수 있는 문제다.해결 방안은 사용 신호량이다.그러나 코드가 너무 복잡해져서 우리가 이루고 싶은 간단한 목표를 이룰 수 없을까 봐 걱정된다.maxWorkers = runtime.GOMAXPROCS(0)
sem = semaphore.NewWeighted(int64(maxWorkers))
ctx := context.TODO()
for {
if err := sem.Acquire(ctx, 4); err != nil {
log.Printf("Failed to acquire semaphore: %v", err)
break
}
go func() {
defer sem.Release(2)
yesChan <- bytes.Repeat([]byte(expletive), 100000000)
}()
go func() {
defer sem.Release(2)
os.Stdout.Write(<-yesChan)
}()
}
그래서 이것은 틀림없이 폭탄일 거야, 그렇지?얼마나 빠른지 봅시다...서스펜스
[1.42GiB/s]
왜?왜 그것은 실제로 이전의 직렬 작업에서 1억 번의 쓰기 작업과 같습니까?
답은 간단하다. 협동 루트가 병행 작업이라고 해도 I/O 작업이 아니기 때문에 개선은 거의 뚜렷하지 않다.동일한 출력을 동시에 두 번 쓸 수 없습니다.이것은 틀림없이 직렬 조작일 것이다. 이것이 바로 왜 지나치게 복잡한goroutines 코드가 여기에 도움이 되지 않는가이다.
실현 4: 바이트는 우리의 마지막 희망이다
지금까지 우리는 문자열을 사용해 왔다는 것을 주의하십시오.우리는 그것을 바이트로 바꾸었다
yesChan = make(chan []byte)
go func() {
defer sem.Release(2)
yesChan <- bytes.Repeat([]byte(expletive), 100000000)
}()
go func() {
defer sem.Release(2)
os.Stdout.Write(<-yesChan)
}()
우리는 무슨 개선이 있습니까?맞다(하지만 많지 않음) [1.63GiB/s] 따라서 문자열이 아닌 바이트를 사용하는 것이 도움이 된다.구현 5: 기본으로 돌아가기
Goroutine/Semaphore 혼란이 필요하지 않기 때문에 이 스크립트를 기반으로 가져올 수 있습니다.
for {
os.Stdout.Write(bytes.Repeat([]byte(expletive), 100000000))
}
이것이 바로 우리가 1.63GiB/s를 실현하는 데 필요한 전부입니다!그 밖에 당신의 이정수를 고려하면 다를 수 있습니다. 왜냐하면 이 기능을 실행하는 컴퓨터도 일정한 역할을 하기 때문입니다.나는 다른 사람의 코드를 실행해 보았는데, 보도에 의하면 속도가 약 4GiB/s라고 하는데, 나는 절반밖에 얻지 못했기 때문에, 만약 당신이 당신의 기계에서 그것을 실행하고 싶다면, 고려해 보세요.
실현 6: 바이트 버퍼가 도움이 됩니까?
요컨대 적어도 나의 중등 지식으로 말하자면, 그것은 아닌 것 같다.
for {
b.Write(bytes.Repeat([]byte(expletive), 100000000))
b.WriteTo(os.Stdout)
}
즉, [1.28GiB/s]실현 7: 적당한bufio 버퍼 어때요?
맞다이것은 내가 사용하는 프로세서와 메모리가 달성할 수 있는 가장 빠른 속도이다.
for {
f := bufio.NewWriterSize(os.Stdout, 8192)
f.Write(bytes.Repeat([]byte(expletive+"\n"), 100000000))
}
이것은 나에게 [1.73GiB/s]지금까지 나는 더 많은 것을 실현할 수 없었다. 만약 당신이 더 좋은 힌트를 가지고 있다면 나에게 알려주세요.전체 버전 7 구현(최대)
이것은 완전한 코드다.대부분 명령줄 플래그와 관련된 템플릿 파일로 내용이 매우 적다.
package main
import (
"bytes"
"github.com/urfave/cli"
"os"
"bufio"
)
func yes() *cli.App {
app := cli.NewApp()
app.Name = "yes"
app.Usage = "yes outputs expletive, or, by default, ``y'', forever"
app.Action = func(c *cli.Context) error {
expletive := c.Args().Get(0)
if expletive == "" {
expletive = "y"
}
for {
f := bufio.NewWriterSize(os.Stdout, 8192)
f.Write(bytes.Repeat([]byte(expletive+"\n"), 100000000))
}
}
return app
}
func main() {
app := yes()
app.Run(os.Args)
}
한층 더 읽다
Reference
이 문제에 관하여(Go Quick Clone "yes"사용), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/rubenwap/making-a-fast-yes-clone-with-go-hom텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)