-
Notifications
You must be signed in to change notification settings - Fork 0
Add hw3_bench solution #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Neodelf
wants to merge
1
commit into
master
Choose a base branch
from
hw3_bench
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"regexp" | ||
"strings" | ||
// "log" | ||
) | ||
|
||
const filePath string = "./data/users.txt" | ||
|
||
func SlowSearch(out io.Writer) { | ||
file, err := os.Open(filePath) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
fileContents, err := ioutil.ReadAll(file) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
r := regexp.MustCompile("@") | ||
seenBrowsers := []string{} | ||
uniqueBrowsers := 0 | ||
foundUsers := "" | ||
|
||
lines := strings.Split(string(fileContents), "\n") | ||
|
||
users := make([]map[string]interface{}, 0) | ||
for _, line := range lines { | ||
user := make(map[string]interface{}) | ||
// fmt.Printf("%v %v\n", err, line) | ||
err := json.Unmarshal([]byte(line), &user) | ||
if err != nil { | ||
panic(err) | ||
} | ||
users = append(users, user) | ||
} | ||
|
||
for i, user := range users { | ||
|
||
isAndroid := false | ||
isMSIE := false | ||
|
||
browsers, ok := user["browsers"].([]interface{}) | ||
if !ok { | ||
// log.Println("cant cast browsers") | ||
continue | ||
} | ||
|
||
for _, browserRaw := range browsers { | ||
browser, ok := browserRaw.(string) | ||
if !ok { | ||
// log.Println("cant cast browser to string") | ||
continue | ||
} | ||
if ok, err := regexp.MatchString("Android", browser); ok && err == nil { | ||
isAndroid = true | ||
notSeenBefore := true | ||
for _, item := range seenBrowsers { | ||
if item == browser { | ||
notSeenBefore = false | ||
} | ||
} | ||
if notSeenBefore { | ||
// log.Printf("SLOW New browser: %s, first seen: %s", browser, user["name"]) | ||
seenBrowsers = append(seenBrowsers, browser) | ||
uniqueBrowsers++ | ||
} | ||
} | ||
} | ||
|
||
for _, browserRaw := range browsers { | ||
browser, ok := browserRaw.(string) | ||
if !ok { | ||
// log.Println("cant cast browser to string") | ||
continue | ||
} | ||
if ok, err := regexp.MatchString("MSIE", browser); ok && err == nil { | ||
isMSIE = true | ||
notSeenBefore := true | ||
for _, item := range seenBrowsers { | ||
if item == browser { | ||
notSeenBefore = false | ||
} | ||
} | ||
if notSeenBefore { | ||
// log.Printf("SLOW New browser: %s, first seen: %s", browser, user["name"]) | ||
seenBrowsers = append(seenBrowsers, browser) | ||
uniqueBrowsers++ | ||
} | ||
} | ||
} | ||
|
||
if !(isAndroid && isMSIE) { | ||
continue | ||
} | ||
|
||
// log.Println("Android and MSIE user:", user["name"], user["email"]) | ||
email := r.ReplaceAllString(user["email"].(string), " [at] ") | ||
foundUsers += fmt.Sprintf("[%d] %s <%s>\n", i, user["name"], email) | ||
} | ||
|
||
fmt.Fprintln(out, "found users:\n"+foundUsers) | ||
fmt.Fprintln(out, "Total unique browsers", len(seenBrowsers)) | ||
} |
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
package main | ||
|
||
import ( | ||
json "encoding/json" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"strings" | ||
"sync" | ||
|
||
easyjson "github.com/mailru/easyjson" | ||
jlexer "github.com/mailru/easyjson/jlexer" | ||
jwriter "github.com/mailru/easyjson/jwriter" | ||
) | ||
|
||
var userPool = sync.Pool{ | ||
New: func() interface{} { | ||
return User{} | ||
}, | ||
} | ||
|
||
type User struct { | ||
Email string `json:"email"` | ||
Browsers []string `json:"browsers"` | ||
Name string `json:"name"` | ||
} | ||
|
||
func FastSearch(out io.Writer) { | ||
file, err := os.Open(filePath) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
fileContents, err := ioutil.ReadAll(file) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
seenBrowsers := []string{} | ||
uniqueBrowsers := 0 | ||
foundUsers := "" | ||
|
||
lines := strings.Split(string(fileContents), "\n") | ||
|
||
for i, line := range lines { | ||
user := User{} | ||
//err := json.Unmarshal([]byte(line), &user) | ||
err := user.UnmarshalJSON([]byte(line)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
isAndroid := false | ||
isMSIE := false | ||
|
||
for _, browser := range user.Browsers { | ||
contansAndroid := strings.Contains(browser, "Android") | ||
contansMSIE := strings.Contains(browser, "MSIE") | ||
|
||
if contansAndroid || contansMSIE { | ||
if contansAndroid { | ||
isAndroid = true | ||
} | ||
|
||
if contansMSIE { | ||
isMSIE = true | ||
} | ||
|
||
notSeenBefore := true | ||
for _, item := range seenBrowsers { | ||
if item == browser { | ||
notSeenBefore = false | ||
break | ||
} | ||
} | ||
if notSeenBefore { | ||
// log.Printf("SLOW New browser: %s, first seen: %s", browser, user["name"]) | ||
seenBrowsers = append(seenBrowsers, browser) | ||
uniqueBrowsers++ | ||
} | ||
} | ||
} | ||
|
||
if !(isAndroid && isMSIE) { | ||
continue | ||
} | ||
|
||
// log.Println("Android and MSIE user:", user["name"], user["email"]) | ||
email := strings.Replace(user.Email, "@", " [at] ", -1) | ||
foundUsers += fmt.Sprintf("[%d] %s <%s>\n", i, user.Name, email) | ||
} | ||
|
||
fmt.Fprintln(out, "found users:\n"+foundUsers) | ||
fmt.Fprintln(out, "Total unique browsers", len(seenBrowsers)) | ||
} | ||
|
||
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. | ||
|
||
// suppress unused package warning | ||
var ( | ||
_ *json.RawMessage | ||
_ *jlexer.Lexer | ||
_ *jwriter.Writer | ||
_ easyjson.Marshaler | ||
) | ||
|
||
func easyjson3486653aDecodeHw3Bench(in *jlexer.Lexer, out *User) { | ||
isTopLevel := in.IsStart() | ||
if in.IsNull() { | ||
if isTopLevel { | ||
in.Consumed() | ||
} | ||
in.Skip() | ||
return | ||
} | ||
in.Delim('{') | ||
for !in.IsDelim('}') { | ||
key := in.UnsafeString() | ||
in.WantColon() | ||
if in.IsNull() { | ||
in.Skip() | ||
in.WantComma() | ||
continue | ||
} | ||
switch key { | ||
case "email": | ||
out.Email = string(in.String()) | ||
case "browsers": | ||
if in.IsNull() { | ||
in.Skip() | ||
out.Browsers = nil | ||
} else { | ||
in.Delim('[') | ||
if out.Browsers == nil { | ||
if !in.IsDelim(']') { | ||
out.Browsers = make([]string, 0, 4) | ||
} else { | ||
out.Browsers = []string{} | ||
} | ||
} else { | ||
out.Browsers = (out.Browsers)[:0] | ||
} | ||
for !in.IsDelim(']') { | ||
var v1 string | ||
v1 = string(in.String()) | ||
out.Browsers = append(out.Browsers, v1) | ||
in.WantComma() | ||
} | ||
in.Delim(']') | ||
} | ||
case "name": | ||
out.Name = string(in.String()) | ||
default: | ||
in.SkipRecursive() | ||
} | ||
in.WantComma() | ||
} | ||
in.Delim('}') | ||
if isTopLevel { | ||
in.Consumed() | ||
} | ||
} | ||
func easyjson3486653aEncodeHw3Bench(out *jwriter.Writer, in User) { | ||
out.RawByte('{') | ||
first := true | ||
_ = first | ||
{ | ||
const prefix string = ",\"email\":" | ||
if first { | ||
first = false | ||
out.RawString(prefix[1:]) | ||
} else { | ||
out.RawString(prefix) | ||
} | ||
out.String(string(in.Email)) | ||
} | ||
{ | ||
const prefix string = ",\"browsers\":" | ||
if first { | ||
first = false | ||
out.RawString(prefix[1:]) | ||
} else { | ||
out.RawString(prefix) | ||
} | ||
if in.Browsers == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { | ||
out.RawString("null") | ||
} else { | ||
out.RawByte('[') | ||
for v2, v3 := range in.Browsers { | ||
if v2 > 0 { | ||
out.RawByte(',') | ||
} | ||
out.String(string(v3)) | ||
} | ||
out.RawByte(']') | ||
} | ||
} | ||
{ | ||
const prefix string = ",\"name\":" | ||
if first { | ||
first = false | ||
out.RawString(prefix[1:]) | ||
} else { | ||
out.RawString(prefix) | ||
} | ||
out.String(string(in.Name)) | ||
} | ||
out.RawByte('}') | ||
} | ||
|
||
// MarshalJSON supports json.Marshaler interface | ||
func (v User) MarshalJSON() ([]byte, error) { | ||
w := jwriter.Writer{} | ||
easyjson3486653aEncodeHw3Bench(&w, v) | ||
return w.Buffer.BuildBytes(), w.Error | ||
} | ||
|
||
// MarshalEasyJSON supports easyjson.Marshaler interface | ||
func (v User) MarshalEasyJSON(w *jwriter.Writer) { | ||
easyjson3486653aEncodeHw3Bench(w, v) | ||
} | ||
|
||
// UnmarshalJSON supports json.Unmarshaler interface | ||
func (v *User) UnmarshalJSON(data []byte) error { | ||
r := jlexer.Lexer{Data: data} | ||
easyjson3486653aDecodeHw3Bench(&r, v) | ||
return r.Error() | ||
} | ||
|
||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface | ||
func (v *User) UnmarshalEasyJSON(l *jlexer.Lexer) { | ||
easyjson3486653aDecodeHw3Bench(l, v) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
Есть функиця, которая что-то там ищет по файлу. Но делает она это не очень быстро. Надо её оптимизировать. | ||
|
||
Задание на работу с профайлером pprof. | ||
|
||
Цель задания - научиться работать с pprof, находить горячие места в коде, уметь строить профиль потребления cpu и памяти, оптимизировать код с учетом этой информации. Написание самого быстрого решения не является целью задания. | ||
|
||
Для генерации графа вам понадобится graphviz. Для пользователей windows не забудьте добавить его в PATH чтобы была доступна команда dot. | ||
|
||
Рекомендую внимательно прочитать доп. материалы на русском - там ещё много примеров оптимизации и объяснений как работать с профайлером. Фактически там есть вся информация для выполнения этого задания. | ||
|
||
Есть с десяток мест где можно оптимизировать. | ||
|
||
Для выполнения задания необходимо чтобы один из параметров ( ns/op, B/op, allocs/op ) был быстрее чем в *BenchmarkSolution* ( fast < solution ) и ещё один лучше *BenchmarkSolution* + 20% ( fast < solution * 1.2), например ( fast allocs/op < 10422*1.2=12506 ). | ||
|
||
По памяти ( B/op ) и количеству аллокаций ( allocs/op ) можно ориентироваться ровно на результаты *BenchmarkSolution* ниже, по времени ( ns/op ) - нет, зависит от системы. | ||
|
||
Для этого задания увеличено количество проверок с 3-х до 5 за 8 часов. | ||
|
||
Результат в fast.go в функцию FastSearch (изначально там то же самое что в SlowSearch). | ||
|
||
Пример результатов с которыми будет сравниваться: | ||
``` | ||
$ go test -bench . -benchmem | ||
|
||
goos: windows | ||
|
||
goarch: amd64 | ||
|
||
BenchmarkSlow-8 10 142703250 ns/op 336887900 B/op 284175 allocs/op | ||
|
||
BenchmarkSolution-8 500 2782432 ns/op 559910 B/op 10422 allocs/op | ||
|
||
PASS | ||
|
||
ok coursera/hw3 3.897s | ||
``` | ||
|
||
Запуск: | ||
* `go test -v` - чтобы проверить что ничего не сломалось | ||
* `go test -bench . -benchmem` - для просмотра производительности | ||
|
||
Советы: | ||
* Смотрите где мы аллоцируем память | ||
* Смотрите где мы накапливаем весь результат, хотя нам все значения одновременно не нужны | ||
* Смотрите где происходят преобразования типов, которые можно избежать | ||
* Смотрите не только на графе, но и в pprof в текстовом виде (list FastSearch) - там прямо по исходнику можно увидеть где что | ||
* Задание предполагает использование easyjson. На сервере эта библиотека есть, подключать можно. Но сгенерированный через easyjson код вам надо поместить в файл с вашей функцией | ||
* Можно сделать без easyjson | ||
|
||
Примечание: | ||
* easyjson основан на рефлекции и не может работать с пакетом main. Для генерации кода вам необходимо вынести вашу структуру в отдельный пакет, сгенерить там код, потом забрать его в main |
Binary file not shown.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exported function SlowSearch should have comment or be unexported