Golang代码运行比PHP中的相同代码慢得多。

huangapple go评论72阅读模式
英文:

Golang code running really slow than same code in PHP

问题

新手学习Golang,昨天我开始尝试使用Golang并编写了一些实际上是用PHP编写的代码。我只是想看看性能上的差异。

我在PHP中做了完全相同的事情,HTTP请求的响应也是完全相同的,但是即使编译后,Golang的性能仍然非常慢。

我正在努力理解在Golang中使用了哪些东西,我不应该使用哪些东西,以及如何改进这段代码的性能。

我知道遍历map是很慢的,但是PHP使用哈希映射来实现多维数组,我可以保证我使用的SQL查询是从PHP中粘贴复制过来的,机器是相同的,循环次数在两种代码中也是相同的。

编辑:
将代码更改为使用静态类型的结构体而不是使用map,并确定了有问题的代码块。

新代码:

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"net/http"
	"strings"
)

type listingsType struct {
	TicketId    string
	DateCreated string
}

func main() {
	db, err := sql.Open("mysql", "******")

	checkErr(err)
	fmt.Println("Handle Request setup... OK")
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {

		jsonData, err := getListings(db)
		checkErr(err)

		w.Write([]byte(jsonData))

	})
	fmt.Println("Starting Server....")
	fmt.Println("Listening on port 8081")
	http.ListenAndServe(":8081", nil)

}

func getListings(db *sql.DB) ([]byte, error) {
	var userId string = "142"

	normalListings := sqlToArray(db, `*****`)

	manualListings := sqlToArray(db, "*****")

	var groupIds []string

	for _, elem := range manualListings {
		groupId := "142," + elem.DateCreated
		if !stringInSlice(groupId, groupIds) {
			groupIds = append(groupIds, groupId)
		}

	}

	var groupIdsString string
	groupIdsString = strings.Join(groupIds, ", ")
	fmt.Println(groupIdsString)
	manualGroups := sqlToArray(db, "******")

	for _, manualList := range manualListings {

		for _, manualGroup := range manualGroups {
			groupId := "142," + manualList.DateCreated

			if groupId == manualGroup.TicketId {
				var entry listingsType
				entry.TicketId = manualList.TicketId
				entry.DateCreated = manualGroup.DateCreated
				normalListings = append(normalListings, entry)

			}
		}
	}

	return json.Marshal(normalListings)

}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func sqlToArray(db *sql.DB, sqlString string) []listingsType {

	rows, err := db.Query(sqlString)
	checkErr(err)

	tableData := []listingsType{}

	for rows.Next() {

		var entry listingsType
		rows.Scan(&entry.TicketId, &entry.DateCreated)

		tableData = append(tableData, entry)

	}

	return tableData

}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

有问题的代码块:
当我注释掉以下代码块时,我的代码表现良好。

对于这个循环有什么问题吗?

for _, manualList := range manualListings {

		for _, manualGroup := range manualGroups {
			groupId := "142," + manualList.DateCreated

			if groupId == manualGroup.TicketId {
				var entry listingsType
				entry.TicketId = manualList.TicketId
				entry.DateCreated = manualGroup.DateCreated
				normalListings = append(normalListings, entry)

			}
		}
	}

性能分析结果:

Golang代码运行比PHP中的相同代码慢得多。

英文:

New to Golang, Yesterday I've started to play with Golang and wrote some code which was actually written in PHP. I just wanted to see difference in performance.

I am doing the exact same thing in PHP response is exact same in http request but the Golang is performing really slow even after compiling it.

I am trying to understand what things that I am using in Golang I shouldn't be using and how can I improve performance in this piece of Code.

I know Iterating over map is slow but PHP using hash maps for implementing multidimentional arrays, well. I can gurantee the sql queries I used were exact same copy pasted from PHP, machines are same, and loop numbers are same in both codes.

package main
import (
"database/sql"
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
"net/http"
"reflect"
"strings"
)
func main() {
db, err := sql.Open("mysql", "***:****@tcp(****:3306)/****")
fmt.Println(reflect.TypeOf(db))
checkErr(err)
fmt.Println("Handle Request setup... OK")
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
jsonData, err := getListings(db)
checkErr(err)
w.Write([]byte(jsonData))
})
fmt.Println("Starting Server....")
fmt.Println("Listening on port 8081")
http.ListenAndServe(":8081", nil)
}
func getListings(db *sql.DB) ([]byte, error) {
var userId string = "142"
normalListings := sqlToArray(db, `******`)
manualListings := sqlToArray(db, "******")
var groupIds []string
for key := range manualListings {
groupId := "142," + manualListings[key]["group_id"]
if !stringInSlice(groupId, groupIds) {
groupIds = append(groupIds, groupId)
}
}
var groupIdsString string
groupIdsString = strings.Join(groupIds, ", ")
manualGroups := sqlToArray(db, "*****")
for key := range manualListings {
for key2 := range manualGroups {
groupId := "142," + manualListings[key]["group_id"]
if groupId == manualGroups[key]["ticket_id"] {
entry := make(map[string]string)
entry["ticket_id"] = manualListings[key]["listing_id"]
entry["date_created"] = manualGroups[key2]["date_created"]
normalListings = append(normalListings, entry)
}
}
}
return json.Marshal(normalListings)
}
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
func sqlToArray(db *sql.DB, sqlString string) []map[string]string {
rows, err := db.Query(sqlString)
checkErr(err)
columns, err := rows.Columns()
count := len(columns)
values := make([]interface{}, count)
valuePtrs := make([]interface{}, count)
tableData := make([]map[string]string, 0)
for rows.Next() {
for i := 0; i < count; i++ {
valuePtrs[i] = &values[i]
}
rows.Scan(valuePtrs...)
entry := make(map[string]string)
for i, col := range columns {
val := values[i]
b, ok := val.([]byte)
if ok {
entry[col] = string(b)
} else {
entry[col] = string(b)
}
}
tableData = append(tableData, entry)
}
return tableData
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}

Edits:
Changed the code to use statically typed structs instead of using maps and Identified the problematic piece of code

New code:

package main
import (
"database/sql"
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
"net/http"
"strings"
)
type listingsType struct {
TicketId    string
DateCreated string
}
func main() {
db, err := sql.Open("mysql", "******")
checkErr(err)
fmt.Println("Handle Request setup... OK")
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
jsonData, err := getListings(db)
checkErr(err)
w.Write([]byte(jsonData))
})
fmt.Println("Starting Server....")
fmt.Println("Listening on port 8081")
http.ListenAndServe(":8081", nil)
}
func getListings(db *sql.DB) ([]byte, error) {
var userId string = "142"
normalListings := sqlToArray(db, `*****`)
manualListings := sqlToArray(db, "*****")
var groupIds []string
for _, elem := range manualListings {
groupId := "142," + elem.DateCreated
if !stringInSlice(groupId, groupIds) {
groupIds = append(groupIds, groupId)
}
}
var groupIdsString string
groupIdsString = strings.Join(groupIds, ", ")
fmt.Println(groupIdsString)
manualGroups := sqlToArray(db, "******")
for _, manualList := range manualListings {
for _, manualGroup := range manualGroups {
groupId := "142," + manualList.DateCreated
if groupId == manualGroup.TicketId {
var entry listingsType
entry.TicketId = manualList.TicketId
entry.DateCreated = manualGroup.DateCreated
normalListings = append(normalListings, entry)
}
}
}
return json.Marshal(normalListings)
}
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
func sqlToArray(db *sql.DB, sqlString string) []listingsType {
rows, err := db.Query(sqlString)
checkErr(err)
tableData := []listingsType{}
for rows.Next() {
var entry listingsType
rows.Scan(&entry.TicketId, &entry.DateCreated)
tableData = append(tableData, entry)
}
return tableData
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}

Problematic piece of code
As soon as I comment the following block of code the my code performs just fine.

Any idea what is wrong with this loop ?

for _, manualList := range manualListings {
for _, manualGroup := range manualGroups {
groupId := "142," + manualList.DateCreated
if groupId == manualGroup.TicketId {
var entry listingsType
entry.TicketId = manualList.TicketId
entry.DateCreated = manualGroup.DateCreated
normalListings = append(normalListings, entry)
}
}
}

Profiling Result

Golang代码运行比PHP中的相同代码慢得多。

答案1

得分: 0

好的,以下是翻译好的内容:

好的,顺便说一下,我解决了这个问题。我将请求时间从5k+毫秒降低到500毫秒,现在我的PHP代码最终变得更慢了,大约是900毫秒

我通过实现一个单独的函数来获取SQL数据,并将其以键值对的形式存储在不同的数据结构中,而不是在整个数组中进行搜索,从而摆脱了内部循环的搜索。我将我要在数组中查找的值作为键创建了值,这样我就摆脱了通过线性搜索字符串来造成麻烦的第二个循环。

manualGroups := sqlToArraySpecial(db, "****")

for _, manualList := range manualListings {
    //index := stringInSliceArray(manualList.DateCreated, manualGroups)
    groupId := "142," + manualList.DateCreated
    var entry listingsType
    entry.TicketId = manualList.TicketId
    entry.DateCreated = manualGroups[groupId]
    normalListings = append(normalListings, entry)
}

这是我的新的SQL函数:

func sqlToArraySpecial(db *sql.DB, sqlString string) map[string]string {

    rows, err := db.Query(sqlString)
    checkErr(err)

    tableData := make(map[string]string)

    for rows.Next() {

        var date_created string
        var ticket_id string

        rows.Scan(&ticket_id, &date_created)
        //fmt.Println(ticket_id)
        tableData[ticket_id] = date_created

    }

    return tableData

}
英文:

Ok So got it fixed by the way. I brought the request time from 5k+ MS to 500 MS, now finally my PHP code is slower which is 900 MS

I got rid of the inner loop to search by implementing a separate function to get SQL data in a different data structure in key value of maps instead of searching whole arrays I created the value as key which I was looking for in array, This way I got rid of the second loop which was making trouble by linear search on strings.

manualGroups := sqlToArraySpecial(db, "****")
for _, manualList := range manualListings {
//index := stringInSliceArray(manualList.DateCreated, manualGroups)
groupId := "142," + manualList.DateCreated
var entry listingsType
entry.TicketId = manualList.TicketId
entry.DateCreated = manualGroups[groupId]
normalListings = append(normalListings, entry)
}

and here is my new SQL function

func sqlToArraySpecial(db *sql.DB, sqlString string) map[string]string {
rows, err := db.Query(sqlString)
checkErr(err)
tableData := make(map[string]string)
for rows.Next() {
var date_created string
var ticket_id string
rows.Scan(&ticket_id, &date_created)
//fmt.Println(ticket_id)
tableData[ticket_id] = date_created
}
return tableData
}

答案2

得分: 0

尽管这是一个已经过时的帖子,但我还是想指出一下,因为没有其他人(明确地)提到过,而且这是一个很重要的知识点:

  • 嵌套的for循环展示了二次运行时间复杂度,
  • 正如你所说,搜索一个数组需要线性时间,

所以简单来说:

  • 计算时间将会随着元素总数的平方而增加。

现在来回答为什么在PHP中不是这种情况——因为你使用了一个哈希映射:

  • 可以说,哈希映射展示了常数时间复杂度。

再次简单来说,这意味着:

  • 查找时间与元素数量(也就是集合的大小)无关。

参见:大O符号

话虽如此,请注意:

  • 我不懂PHP,
    • 因此不了解语言如何实现数组,以及
  • 我不是算法专家,

所以请将我的帖子视为对“一般”情况的陈述。

祝好

英文:

although this is a dead post, I cannot help but note since no one else has (explicitly), and it's kinda' important to know why:

  • nested for loops exhibit quadratic running time complexity,
  • and as you stated, searching an array takes linear time,

so simply put:

  • the computation time will increase by the square of the total number of elements.

now to answer why this isn't the case in php -- well cause you were using a hash map which:

  • can be said, exhibits a constant time complexity

again, simply put this means that:

  • look up time is not related to the number of elements (aka the size of the collection).

see: big-o

with all that being said, please note:

  • i don't know php,
    • thus do not the know details regarding how the language implements arrays, and
  • im not an algorithm expert,

so please treat my post as a statement for the general case.

pce

huangapple
  • 本文由 发表于 2016年9月11日 22:41:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/39437216.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定