我有一个微服务,为员工提供工作日历。原始数据具有以下文件结构:
calendars / [year] / [employee].json
字符串
[employee].json的内容:
{
// [date]: [hours]
"2023-02-01": 7,
"2023-02-02": 7,
}
型
使用Go,我将所有数据转换为这两个类型为CalendarFast
和CalendarSlow
的变量:
type Username = string
type Date = string
type Year = string
type Hours = float64
type CalendarFast map[Username]map[Year][]struct {
Time time.Time // "2022-01-01"
Hours Hours // 7
}
type CalendarSlow map[Username]map[Year]map[Date]Hours
型
例如,如果我想从我的API返回给定日期范围内的所有小时数,我可以编写这两个基准来测试哪种数据结构更好:
func BenchmarkNew(b *testing.B) {
c := make(CalendarFast)
c.Update(context.TODO())
from, _ := time.Parse("2006-01-02", "2023-01-01")
to, _ := time.Parse("2006-01-02", "2023-02-01")
isInRange := func(t, from, to time.Time) bool {
return (t.After(from) && t.Before(to)) || t.Equal(from) || t.Equal(to)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
// result is map[username]map[date]hours
result := make(map[string]map[string]float64)
for username, years := range c {
if _, ok := result[username]; !ok {
result[username] = make(map[string]float64)
}
for _, dates := range years {
for i, n := 0, len(dates); i < n; i++ {
if isInRange(dates[i].Time, from, to) {
result[username][dates[i].Time.String()] = dates[i].Hours
}
}
}
}
}
}
func BenchmarkOld(b *testing.B) {
c := make(CalendarSlow)
c.Update(context.TODO())
from, _ := time.Parse("2006-01-02", "2023-01-01")
to, _ := time.Parse("2006-01-02", "2023-02-01")
b.ResetTimer()
for i := 0; i < b.N; i++ {
result := make(map[string]map[string]float64)
for username, years := range c {
if _, ok := result[username]; !ok {
result[username] = make(map[string]float64)
}
for _, items := range years {
for date, hours := range items {
t, err := time.Parse("2006-01-02", date)
if err != nil {
continue
}
if (t.After(from) && t.Before(to)) || t.Equal(from) || t.Equal(to) {
result[username][date] = hours
}
}
}
}
}
}
型
我有以下基准测试,它表明在数组中搜索范围要快得多,搜索map中的每个元素:
BenchmarkNew-4 285 4208045 ns/op 692965 B/op 6303 allocs/op
BenchmarkOld-4 39 29672935 ns/op 621877 B/op 1407 allocs/op
型
我的问题是:如何进一步提高性能?我是否应该将数据结构更改为另一个?
我需要速度,因为我收到了很多对我当前PHP API的调用,并发现Go中的微服务可以轻松处理1 k rps,而k8s中只有10 m cpu和64 Mib内存
我已经对“time.Time”方法的数组感到满意,但对另一种实现感到好奇。
UPD:
| 迭代|时间| time |
| --|--| ------------ |
| 二百二十九|5259548 ns/op| 5259548 ns/op |
| 四一四|2892932 ns/op| 2892932 ns/op |
1条答案
按热度按时间bnl4lu3b1#
根据您的描述,数据是静态的,您只在程序启动时阅读源文件一次。
为什么不对每个员工的日期数组进行排序,这样一旦超过了结束日期,就可以停止检查其余的日期?
为什么
year
Map存在?。还有一件事要重复。为什么要将所有日期存储为字符串,然后每次要按日期范围进行过滤时都要解析它们?为什么不使用
time.Time
?在内存中更有效,并且不需要一遍又一遍地解析。我将使用
map[Username][]struct{date time.Time, hours int}
,在读取一次后对数组进行排序,然后像这样迭代:字符串