Golang HTTP requestsHandling HTTP requestsHTTP Client requests done rightRetry loop for asynchronous HTTP requestsGolang HTTP status checkerUnit testing HTTP requestsProxying Socket.io requests and other HTTP requestsGolang Tour Web Crawler ExerciseGolang HTTP request retry codeRepeatable HTTP requests in PythonMaking multiple HTTP requests to Twitter
How to write Quadratic equation with negative coefficient
What does "tick" mean in this sentence?
Would a primitive species be able to learn English from reading books alone?
Why is the sun approximated as a black body at ~ 5800 K?
Would this string work as string?
If the only attacker is removed from combat, is a creature still counted as having attacked this turn?
Giving feedback to someone without sounding prejudiced
In One Punch Man, is King actually weak?
El Dorado Word Puzzle II: Videogame Edition
Animation: customize bounce interpolation
If A is dense in Q, then it must be dense in R.
How to preserve electronics (computers, iPads and phones) for hundreds of years
Showing mass murder in a kid's book
Review your own paper in Mathematics
Do I have to take mana from my deck or hand when tapping a dual land?
Has the laser at Magurele, Romania reached a tenth of the Sun's power?
How do you justify more code being written by following clean code practices?
Are Captain Marvel's powers affected by Thanos breaking the Tesseract and claiming the stone?
SOQL query causes internal Salesforce error
Determining multivariate least squares with constraint
Why do Radio Buttons not fill the entire outer circle?
Why does a 97 / 92 key piano exist by Bösendorfer?
I'm just a whisper. Who am I?
How to get directions in deep space?
Golang HTTP requests
Handling HTTP requestsHTTP Client requests done rightRetry loop for asynchronous HTTP requestsGolang HTTP status checkerUnit testing HTTP requestsProxying Socket.io requests and other HTTP requestsGolang Tour Web Crawler ExerciseGolang HTTP request retry codeRepeatable HTTP requests in PythonMaking multiple HTTP requests to Twitter
$begingroup$
I'm beginning to learn about Golang and I would like to have some advice about the following program.
package main
import (
"fmt"
"net/http"
"time"
)
const BenchmarkTry = 1000
type PageBenchmark struct
url string
time int64 // microseconds
func execBenchmark(url string, channel chan PageBenchmark)
totalExecTimeChan := make(chan int64, BenchmarkTry) // set size to prevent blocked goroutine
totalExecTime := int64(0)
// start all the goroutines
for i := 0; i < BenchmarkTry; i++
go execHttpRequest(url, totalExecTimeChan)
// catch new values from totalExecTimeChan values come from execHttpRequest()) and add it to the total
for i := 0; i < BenchmarkTry; i++
totalExecTime += <-totalExecTimeChan // waiting to get a value from one of the goroutines started in the previous for loop
channel <- PageBenchmarkurl, totalExecTime / BenchmarkTry
// exec http request and attach exec time to channel
func execHttpRequest(url string, channel chan int64)
begin := time.Now()
_, _ = http.Get(url)
channel <- time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
func main()
sites := [...]string
pages := [...]string
benchmarkChan := make(chan PageBenchmark, len(sites)*len(pages)) // set size to prevent blocked goroutine
begin := time.Now()
fmt.Println("Beginning !")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarkChan)
// catch new values from benchmarkChan and "print" the PageBenchmark
for i := 0; i < len(sites)*len(pages); i++
benchmark := <-benchmarkChan
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
// print execution time
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Basically, I'm making HTTP requests (GET method) to multiple URLs on multiple web sites. 1000 requests by URL in this example.
For now, I just want to start some goroutines in this order:
Main: start a benchmark (goroutine) for each web site pages, get the average execution time and print it.
Page routine: start 1000 goroutines (benchmark tries), get the execution times from a channel and store the average execution time in an other channel.
Execute an HTTP request on the page and store the execution time in a channel.
This piece of code works but there may be things I'm missing.
- Is the execution scheduling valid?
- Is it appropriate to use defined size channels here?
- Is there a better/more effective way to achieve this task?
multithreading go http benchmarking
New contributor
$endgroup$
add a comment |
$begingroup$
I'm beginning to learn about Golang and I would like to have some advice about the following program.
package main
import (
"fmt"
"net/http"
"time"
)
const BenchmarkTry = 1000
type PageBenchmark struct
url string
time int64 // microseconds
func execBenchmark(url string, channel chan PageBenchmark)
totalExecTimeChan := make(chan int64, BenchmarkTry) // set size to prevent blocked goroutine
totalExecTime := int64(0)
// start all the goroutines
for i := 0; i < BenchmarkTry; i++
go execHttpRequest(url, totalExecTimeChan)
// catch new values from totalExecTimeChan values come from execHttpRequest()) and add it to the total
for i := 0; i < BenchmarkTry; i++
totalExecTime += <-totalExecTimeChan // waiting to get a value from one of the goroutines started in the previous for loop
channel <- PageBenchmarkurl, totalExecTime / BenchmarkTry
// exec http request and attach exec time to channel
func execHttpRequest(url string, channel chan int64)
begin := time.Now()
_, _ = http.Get(url)
channel <- time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
func main()
sites := [...]string
pages := [...]string
benchmarkChan := make(chan PageBenchmark, len(sites)*len(pages)) // set size to prevent blocked goroutine
begin := time.Now()
fmt.Println("Beginning !")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarkChan)
// catch new values from benchmarkChan and "print" the PageBenchmark
for i := 0; i < len(sites)*len(pages); i++
benchmark := <-benchmarkChan
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
// print execution time
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Basically, I'm making HTTP requests (GET method) to multiple URLs on multiple web sites. 1000 requests by URL in this example.
For now, I just want to start some goroutines in this order:
Main: start a benchmark (goroutine) for each web site pages, get the average execution time and print it.
Page routine: start 1000 goroutines (benchmark tries), get the execution times from a channel and store the average execution time in an other channel.
Execute an HTTP request on the page and store the execution time in a channel.
This piece of code works but there may be things I'm missing.
- Is the execution scheduling valid?
- Is it appropriate to use defined size channels here?
- Is there a better/more effective way to achieve this task?
multithreading go http benchmarking
New contributor
$endgroup$
add a comment |
$begingroup$
I'm beginning to learn about Golang and I would like to have some advice about the following program.
package main
import (
"fmt"
"net/http"
"time"
)
const BenchmarkTry = 1000
type PageBenchmark struct
url string
time int64 // microseconds
func execBenchmark(url string, channel chan PageBenchmark)
totalExecTimeChan := make(chan int64, BenchmarkTry) // set size to prevent blocked goroutine
totalExecTime := int64(0)
// start all the goroutines
for i := 0; i < BenchmarkTry; i++
go execHttpRequest(url, totalExecTimeChan)
// catch new values from totalExecTimeChan values come from execHttpRequest()) and add it to the total
for i := 0; i < BenchmarkTry; i++
totalExecTime += <-totalExecTimeChan // waiting to get a value from one of the goroutines started in the previous for loop
channel <- PageBenchmarkurl, totalExecTime / BenchmarkTry
// exec http request and attach exec time to channel
func execHttpRequest(url string, channel chan int64)
begin := time.Now()
_, _ = http.Get(url)
channel <- time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
func main()
sites := [...]string
pages := [...]string
benchmarkChan := make(chan PageBenchmark, len(sites)*len(pages)) // set size to prevent blocked goroutine
begin := time.Now()
fmt.Println("Beginning !")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarkChan)
// catch new values from benchmarkChan and "print" the PageBenchmark
for i := 0; i < len(sites)*len(pages); i++
benchmark := <-benchmarkChan
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
// print execution time
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Basically, I'm making HTTP requests (GET method) to multiple URLs on multiple web sites. 1000 requests by URL in this example.
For now, I just want to start some goroutines in this order:
Main: start a benchmark (goroutine) for each web site pages, get the average execution time and print it.
Page routine: start 1000 goroutines (benchmark tries), get the execution times from a channel and store the average execution time in an other channel.
Execute an HTTP request on the page and store the execution time in a channel.
This piece of code works but there may be things I'm missing.
- Is the execution scheduling valid?
- Is it appropriate to use defined size channels here?
- Is there a better/more effective way to achieve this task?
multithreading go http benchmarking
New contributor
$endgroup$
I'm beginning to learn about Golang and I would like to have some advice about the following program.
package main
import (
"fmt"
"net/http"
"time"
)
const BenchmarkTry = 1000
type PageBenchmark struct
url string
time int64 // microseconds
func execBenchmark(url string, channel chan PageBenchmark)
totalExecTimeChan := make(chan int64, BenchmarkTry) // set size to prevent blocked goroutine
totalExecTime := int64(0)
// start all the goroutines
for i := 0; i < BenchmarkTry; i++
go execHttpRequest(url, totalExecTimeChan)
// catch new values from totalExecTimeChan values come from execHttpRequest()) and add it to the total
for i := 0; i < BenchmarkTry; i++
totalExecTime += <-totalExecTimeChan // waiting to get a value from one of the goroutines started in the previous for loop
channel <- PageBenchmarkurl, totalExecTime / BenchmarkTry
// exec http request and attach exec time to channel
func execHttpRequest(url string, channel chan int64)
begin := time.Now()
_, _ = http.Get(url)
channel <- time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
func main()
sites := [...]string
pages := [...]string
benchmarkChan := make(chan PageBenchmark, len(sites)*len(pages)) // set size to prevent blocked goroutine
begin := time.Now()
fmt.Println("Beginning !")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarkChan)
// catch new values from benchmarkChan and "print" the PageBenchmark
for i := 0; i < len(sites)*len(pages); i++
benchmark := <-benchmarkChan
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
// print execution time
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Basically, I'm making HTTP requests (GET method) to multiple URLs on multiple web sites. 1000 requests by URL in this example.
For now, I just want to start some goroutines in this order:
Main: start a benchmark (goroutine) for each web site pages, get the average execution time and print it.
Page routine: start 1000 goroutines (benchmark tries), get the execution times from a channel and store the average execution time in an other channel.
Execute an HTTP request on the page and store the execution time in a channel.
This piece of code works but there may be things I'm missing.
- Is the execution scheduling valid?
- Is it appropriate to use defined size channels here?
- Is there a better/more effective way to achieve this task?
multithreading go http benchmarking
multithreading go http benchmarking
New contributor
New contributor
edited 3 mins ago
esote
2,78611038
2,78611038
New contributor
asked 2 hours ago
hunominahunomina
61
61
New contributor
New contributor
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
When running your program with the Go race detector, it found no race conditions. That's good!
Benchmark through testing
Is there a better/more effective way to achieve this task?
For benchmarking, it is recommended to use the benchmarking utilities provided by the testing
package.
A lot of this code is boilerplate benchmarking code. With the testing
package you can be more confident in the accuracy of your benchmarks, especially since you're benchmarking goroutines.
(I will leave switching to the testing
package for you.)
Performance bias
sites[site]+pages[page]
Adding strings through the +
operator is slow. This could be biasing your results (ie if you have lots of very long strings), but that also depends on how many sites you test with.
Without knowledge of your full usage, it's hard to tell.
Accuracy
time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
I would instead use .Seconds()
. Any variation on the scale of milliseconds would be meaningless. From testing with one site and two pages, I received response times of 10288 (10s) and 8128 ms (8s) and an end time of 30073 ms (30s).
Rather than converting to int64
I would keep the type as float64
.
String formatting
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Should then be converted to:
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
And, given the switch to float64
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
Should instead be:
fmt.Printf("Url: %snResponse Time: %.0fsnn", benchmark.url,
benchmark.time)
(Notice I use %s
rather than %v
.)
Variable naming
Variables can have shorter names, especially in shorter functions where their purpose is clear.
totalExecTimeChan
becomestimeChan
totalExecTime
becoemstime
BenchmarkTry
becomestries
I would argue that the names tc
and t
would be equally fine for the first two.
Capitalization matters in Go. The variable BenchmarkTry
and the type PageBenchmark
would be exported. In this case it does not make a difference, but for larger programs and packages it would be important.
Miscellaneous
This cast is not needed.
totalExecTime := int64(0)
With the switch to
float64
, it can instead be:totalExecTime := 0.0
Some of your comments extend beyond the 80-character column. It is useful to stay within 80 characters when viewing things split across your screen. Instead, move comments to the line before the code. (Some of these comments are superfluous, but you're learning so that's okay.)
Store
len(sites)*len(pages)
in a constant, rather than recomputing it each time.
Conclusion
Here is the code I ended up with:
package main
import (
"fmt"
"net/http"
"time"
)
const tries = 1000
type pageBenchmark struct
url string
time float64
func execBenchmark(url string, benchmarks chan pageBenchmark)
// prevent blocked goroutine
timeChan := make(chan float64, tries)
time := 0.0
// start all requests
for i := 0; i < tries; i++
go execHTTPRequest(url, timeChan)
// catch new values from execHTTPRequest()
for i := 0; i < tries; i++
// wait to get value from goroutine
time += <-timeChan
benchmarks <- pageBenchmarkurl, time / tries
// exec http request and attach exec time to channel
func execHTTPRequest(url string, timeChan chan float64)
begin := time.Now()
_, _ = http.Get(url)
timeChan <- time.Since(begin).Seconds()
func main()
sites := [...]string
// sites
pages := [...]string
// pages
const length = len(sites) * len(pages)
// set size to prevent blocked goroutine
benchmarks := make(chan pageBenchmark, length)
begin := time.Now()
fmt.Println("Beginning!n")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarks)
// catch and print benchmarks
for i := 0; i < length; i++
b := <-benchmarks
fmt.Printf("Url: %snResponse Time: %.0fsnn", b.url, b.time)
// print total execution time
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
Hope this helps!
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
hunomina is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215883%2fgolang-http-requests%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
When running your program with the Go race detector, it found no race conditions. That's good!
Benchmark through testing
Is there a better/more effective way to achieve this task?
For benchmarking, it is recommended to use the benchmarking utilities provided by the testing
package.
A lot of this code is boilerplate benchmarking code. With the testing
package you can be more confident in the accuracy of your benchmarks, especially since you're benchmarking goroutines.
(I will leave switching to the testing
package for you.)
Performance bias
sites[site]+pages[page]
Adding strings through the +
operator is slow. This could be biasing your results (ie if you have lots of very long strings), but that also depends on how many sites you test with.
Without knowledge of your full usage, it's hard to tell.
Accuracy
time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
I would instead use .Seconds()
. Any variation on the scale of milliseconds would be meaningless. From testing with one site and two pages, I received response times of 10288 (10s) and 8128 ms (8s) and an end time of 30073 ms (30s).
Rather than converting to int64
I would keep the type as float64
.
String formatting
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Should then be converted to:
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
And, given the switch to float64
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
Should instead be:
fmt.Printf("Url: %snResponse Time: %.0fsnn", benchmark.url,
benchmark.time)
(Notice I use %s
rather than %v
.)
Variable naming
Variables can have shorter names, especially in shorter functions where their purpose is clear.
totalExecTimeChan
becomestimeChan
totalExecTime
becoemstime
BenchmarkTry
becomestries
I would argue that the names tc
and t
would be equally fine for the first two.
Capitalization matters in Go. The variable BenchmarkTry
and the type PageBenchmark
would be exported. In this case it does not make a difference, but for larger programs and packages it would be important.
Miscellaneous
This cast is not needed.
totalExecTime := int64(0)
With the switch to
float64
, it can instead be:totalExecTime := 0.0
Some of your comments extend beyond the 80-character column. It is useful to stay within 80 characters when viewing things split across your screen. Instead, move comments to the line before the code. (Some of these comments are superfluous, but you're learning so that's okay.)
Store
len(sites)*len(pages)
in a constant, rather than recomputing it each time.
Conclusion
Here is the code I ended up with:
package main
import (
"fmt"
"net/http"
"time"
)
const tries = 1000
type pageBenchmark struct
url string
time float64
func execBenchmark(url string, benchmarks chan pageBenchmark)
// prevent blocked goroutine
timeChan := make(chan float64, tries)
time := 0.0
// start all requests
for i := 0; i < tries; i++
go execHTTPRequest(url, timeChan)
// catch new values from execHTTPRequest()
for i := 0; i < tries; i++
// wait to get value from goroutine
time += <-timeChan
benchmarks <- pageBenchmarkurl, time / tries
// exec http request and attach exec time to channel
func execHTTPRequest(url string, timeChan chan float64)
begin := time.Now()
_, _ = http.Get(url)
timeChan <- time.Since(begin).Seconds()
func main()
sites := [...]string
// sites
pages := [...]string
// pages
const length = len(sites) * len(pages)
// set size to prevent blocked goroutine
benchmarks := make(chan pageBenchmark, length)
begin := time.Now()
fmt.Println("Beginning!n")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarks)
// catch and print benchmarks
for i := 0; i < length; i++
b := <-benchmarks
fmt.Printf("Url: %snResponse Time: %.0fsnn", b.url, b.time)
// print total execution time
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
Hope this helps!
$endgroup$
add a comment |
$begingroup$
When running your program with the Go race detector, it found no race conditions. That's good!
Benchmark through testing
Is there a better/more effective way to achieve this task?
For benchmarking, it is recommended to use the benchmarking utilities provided by the testing
package.
A lot of this code is boilerplate benchmarking code. With the testing
package you can be more confident in the accuracy of your benchmarks, especially since you're benchmarking goroutines.
(I will leave switching to the testing
package for you.)
Performance bias
sites[site]+pages[page]
Adding strings through the +
operator is slow. This could be biasing your results (ie if you have lots of very long strings), but that also depends on how many sites you test with.
Without knowledge of your full usage, it's hard to tell.
Accuracy
time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
I would instead use .Seconds()
. Any variation on the scale of milliseconds would be meaningless. From testing with one site and two pages, I received response times of 10288 (10s) and 8128 ms (8s) and an end time of 30073 ms (30s).
Rather than converting to int64
I would keep the type as float64
.
String formatting
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Should then be converted to:
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
And, given the switch to float64
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
Should instead be:
fmt.Printf("Url: %snResponse Time: %.0fsnn", benchmark.url,
benchmark.time)
(Notice I use %s
rather than %v
.)
Variable naming
Variables can have shorter names, especially in shorter functions where their purpose is clear.
totalExecTimeChan
becomestimeChan
totalExecTime
becoemstime
BenchmarkTry
becomestries
I would argue that the names tc
and t
would be equally fine for the first two.
Capitalization matters in Go. The variable BenchmarkTry
and the type PageBenchmark
would be exported. In this case it does not make a difference, but for larger programs and packages it would be important.
Miscellaneous
This cast is not needed.
totalExecTime := int64(0)
With the switch to
float64
, it can instead be:totalExecTime := 0.0
Some of your comments extend beyond the 80-character column. It is useful to stay within 80 characters when viewing things split across your screen. Instead, move comments to the line before the code. (Some of these comments are superfluous, but you're learning so that's okay.)
Store
len(sites)*len(pages)
in a constant, rather than recomputing it each time.
Conclusion
Here is the code I ended up with:
package main
import (
"fmt"
"net/http"
"time"
)
const tries = 1000
type pageBenchmark struct
url string
time float64
func execBenchmark(url string, benchmarks chan pageBenchmark)
// prevent blocked goroutine
timeChan := make(chan float64, tries)
time := 0.0
// start all requests
for i := 0; i < tries; i++
go execHTTPRequest(url, timeChan)
// catch new values from execHTTPRequest()
for i := 0; i < tries; i++
// wait to get value from goroutine
time += <-timeChan
benchmarks <- pageBenchmarkurl, time / tries
// exec http request and attach exec time to channel
func execHTTPRequest(url string, timeChan chan float64)
begin := time.Now()
_, _ = http.Get(url)
timeChan <- time.Since(begin).Seconds()
func main()
sites := [...]string
// sites
pages := [...]string
// pages
const length = len(sites) * len(pages)
// set size to prevent blocked goroutine
benchmarks := make(chan pageBenchmark, length)
begin := time.Now()
fmt.Println("Beginning!n")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarks)
// catch and print benchmarks
for i := 0; i < length; i++
b := <-benchmarks
fmt.Printf("Url: %snResponse Time: %.0fsnn", b.url, b.time)
// print total execution time
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
Hope this helps!
$endgroup$
add a comment |
$begingroup$
When running your program with the Go race detector, it found no race conditions. That's good!
Benchmark through testing
Is there a better/more effective way to achieve this task?
For benchmarking, it is recommended to use the benchmarking utilities provided by the testing
package.
A lot of this code is boilerplate benchmarking code. With the testing
package you can be more confident in the accuracy of your benchmarks, especially since you're benchmarking goroutines.
(I will leave switching to the testing
package for you.)
Performance bias
sites[site]+pages[page]
Adding strings through the +
operator is slow. This could be biasing your results (ie if you have lots of very long strings), but that also depends on how many sites you test with.
Without knowledge of your full usage, it's hard to tell.
Accuracy
time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
I would instead use .Seconds()
. Any variation on the scale of milliseconds would be meaningless. From testing with one site and two pages, I received response times of 10288 (10s) and 8128 ms (8s) and an end time of 30073 ms (30s).
Rather than converting to int64
I would keep the type as float64
.
String formatting
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Should then be converted to:
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
And, given the switch to float64
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
Should instead be:
fmt.Printf("Url: %snResponse Time: %.0fsnn", benchmark.url,
benchmark.time)
(Notice I use %s
rather than %v
.)
Variable naming
Variables can have shorter names, especially in shorter functions where their purpose is clear.
totalExecTimeChan
becomestimeChan
totalExecTime
becoemstime
BenchmarkTry
becomestries
I would argue that the names tc
and t
would be equally fine for the first two.
Capitalization matters in Go. The variable BenchmarkTry
and the type PageBenchmark
would be exported. In this case it does not make a difference, but for larger programs and packages it would be important.
Miscellaneous
This cast is not needed.
totalExecTime := int64(0)
With the switch to
float64
, it can instead be:totalExecTime := 0.0
Some of your comments extend beyond the 80-character column. It is useful to stay within 80 characters when viewing things split across your screen. Instead, move comments to the line before the code. (Some of these comments are superfluous, but you're learning so that's okay.)
Store
len(sites)*len(pages)
in a constant, rather than recomputing it each time.
Conclusion
Here is the code I ended up with:
package main
import (
"fmt"
"net/http"
"time"
)
const tries = 1000
type pageBenchmark struct
url string
time float64
func execBenchmark(url string, benchmarks chan pageBenchmark)
// prevent blocked goroutine
timeChan := make(chan float64, tries)
time := 0.0
// start all requests
for i := 0; i < tries; i++
go execHTTPRequest(url, timeChan)
// catch new values from execHTTPRequest()
for i := 0; i < tries; i++
// wait to get value from goroutine
time += <-timeChan
benchmarks <- pageBenchmarkurl, time / tries
// exec http request and attach exec time to channel
func execHTTPRequest(url string, timeChan chan float64)
begin := time.Now()
_, _ = http.Get(url)
timeChan <- time.Since(begin).Seconds()
func main()
sites := [...]string
// sites
pages := [...]string
// pages
const length = len(sites) * len(pages)
// set size to prevent blocked goroutine
benchmarks := make(chan pageBenchmark, length)
begin := time.Now()
fmt.Println("Beginning!n")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarks)
// catch and print benchmarks
for i := 0; i < length; i++
b := <-benchmarks
fmt.Printf("Url: %snResponse Time: %.0fsnn", b.url, b.time)
// print total execution time
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
Hope this helps!
$endgroup$
When running your program with the Go race detector, it found no race conditions. That's good!
Benchmark through testing
Is there a better/more effective way to achieve this task?
For benchmarking, it is recommended to use the benchmarking utilities provided by the testing
package.
A lot of this code is boilerplate benchmarking code. With the testing
package you can be more confident in the accuracy of your benchmarks, especially since you're benchmarking goroutines.
(I will leave switching to the testing
package for you.)
Performance bias
sites[site]+pages[page]
Adding strings through the +
operator is slow. This could be biasing your results (ie if you have lots of very long strings), but that also depends on how many sites you test with.
Without knowledge of your full usage, it's hard to tell.
Accuracy
time.Since(begin).Nanoseconds() / 1000000 // convert to milliseconds
I would instead use .Seconds()
. Any variation on the scale of milliseconds would be meaningless. From testing with one site and two pages, I received response times of 10288 (10s) and 8128 ms (8s) and an end time of 30073 ms (30s).
Rather than converting to int64
I would keep the type as float64
.
String formatting
fmt.Println("End.")
fmt.Println(fmt.Sprintf("%d ms", time.Since(begin).Nanoseconds()/1000000))
Should then be converted to:
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
And, given the switch to float64
fmt.Printf("Url : %vnResponse Time : %d msnn", benchmark.url, benchmark.time)
Should instead be:
fmt.Printf("Url: %snResponse Time: %.0fsnn", benchmark.url,
benchmark.time)
(Notice I use %s
rather than %v
.)
Variable naming
Variables can have shorter names, especially in shorter functions where their purpose is clear.
totalExecTimeChan
becomestimeChan
totalExecTime
becoemstime
BenchmarkTry
becomestries
I would argue that the names tc
and t
would be equally fine for the first two.
Capitalization matters in Go. The variable BenchmarkTry
and the type PageBenchmark
would be exported. In this case it does not make a difference, but for larger programs and packages it would be important.
Miscellaneous
This cast is not needed.
totalExecTime := int64(0)
With the switch to
float64
, it can instead be:totalExecTime := 0.0
Some of your comments extend beyond the 80-character column. It is useful to stay within 80 characters when viewing things split across your screen. Instead, move comments to the line before the code. (Some of these comments are superfluous, but you're learning so that's okay.)
Store
len(sites)*len(pages)
in a constant, rather than recomputing it each time.
Conclusion
Here is the code I ended up with:
package main
import (
"fmt"
"net/http"
"time"
)
const tries = 1000
type pageBenchmark struct
url string
time float64
func execBenchmark(url string, benchmarks chan pageBenchmark)
// prevent blocked goroutine
timeChan := make(chan float64, tries)
time := 0.0
// start all requests
for i := 0; i < tries; i++
go execHTTPRequest(url, timeChan)
// catch new values from execHTTPRequest()
for i := 0; i < tries; i++
// wait to get value from goroutine
time += <-timeChan
benchmarks <- pageBenchmarkurl, time / tries
// exec http request and attach exec time to channel
func execHTTPRequest(url string, timeChan chan float64)
begin := time.Now()
_, _ = http.Get(url)
timeChan <- time.Since(begin).Seconds()
func main()
sites := [...]string
// sites
pages := [...]string
// pages
const length = len(sites) * len(pages)
// set size to prevent blocked goroutine
benchmarks := make(chan pageBenchmark, length)
begin := time.Now()
fmt.Println("Beginning!n")
// start all the goroutines
for site := range sites
for page := range pages
go execBenchmark(sites[site]+pages[page], benchmarks)
// catch and print benchmarks
for i := 0; i < length; i++
b := <-benchmarks
fmt.Printf("Url: %snResponse Time: %.0fsnn", b.url, b.time)
// print total execution time
fmt.Printf("End.nTotal time: %.0fsn", time.Since(begin).Seconds())
Hope this helps!
answered 6 mins ago
esoteesote
2,78611038
2,78611038
add a comment |
add a comment |
hunomina is a new contributor. Be nice, and check out our Code of Conduct.
hunomina is a new contributor. Be nice, and check out our Code of Conduct.
hunomina is a new contributor. Be nice, and check out our Code of Conduct.
hunomina is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215883%2fgolang-http-requests%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown