我需要搜索slice
maps[string]string
个//Search for a giving term
//This function gets the data passed which will need to be search
//and the search term and it will return the matched maps
// the data is pretty simply the map contains { key: andSomeText }
func Search(data []map[string]string, term string) []map[string]string {
set := []map[string]string{}
for _, v := range data {
if v["key"] == term {
set = append(set, v)
}
}
return set
}
。我的想法是,这是一个很好的机会去通道和去常规。
计划是将切片分成几部分并发送并行搜索。 但我有点震惊的是,我的并行版本超时了,而整个切片的搜索都成功了。
我不确定我做错了什么。下面是我用来测试这个概念的代码。真正的代码将涉及更多的复杂性
// All searches all records concurrently
// Has the same function signature as the the search function
// but the main task is to fan out the slice in 5 parts and search
// in parallel
func All(data []map[string]string, term string) []map[string]string {
countOfSlices := 5
part := len(data) / countOfSlices
fmt.Printf("Size of the data:%v\n", len(data))
fmt.Printf("Fragemnt Size:%v\n", part)
timeout := time.After(60000 * time.Millisecond)
c := make(chan []map[string]string)
for i := 0; i < countOfSlices; i++ {
// Fragments of the array passed on to the search method
go func() { c <- Search(data[(part*i):(part*(i+1))], term) }()
}
result := []map[string]string{}
for i := 0; i < part-1; i++ {
select {
case records := <-c:
result = append(result, records...)
case <-timeout:
fmt.Println("timed out!")
return result
}
}
return result
}
因此,搜索给定SearchTerm的地图片段非常有效。
现在我想如果我的切片有20K条目,我想并行搜索
func GenerateTestData(search string) ([]map[string]string, int) {
rand.Seed(time.Now().UTC().UnixNano())
strin := []string{"String One", "This", "String Two", "String Three", "String Four", "String Five"}
var matchCount int
numOfRecords := 20000
set := []map[string]string{}
for i := 0; i < numOfRecords; i++ {
p := rand.Intn(len(strin))
s := strin[p]
if s == search {
matchCount++
}
set = append(set, map[string]string{"key": s})
}
return set, matchCount
}
以下是我的测试:
我有一个生成测试数据和2个测试的函数。
func TestSearchItem(t *testing.T) {
tests := []struct {
InSearchTerm string
Fn func(data []map[string]string, term string) []map[string]string
}{
{
InSearchTerm: "This",
Fn: Search,
},
{InSearchTerm: "This",
Fn: All,
},
}
for i, test := range tests {
startTime := time.Now()
data, expectedMatchCount := GenerateTestData(test.InSearchTerm)
result := test.Fn(data, test.InSearchTerm)
fmt.Printf("Test: [%v]:\nTime: %v \n\n", i+1, time.Since(startTime))
assert.Equal(t, len(result), expectedMatchCount, "expected: %v to be: %v", len(result), expectedMatchCount)
}
}
2个测试:第一个只是遍历切片而第二个是并行搜索
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table "+TABLE_NAME+" (ID INTEGER PRIMARY KEY AUTOINCREMENT, TITLE TEXT, STORY TEXT, AUTHOR TEXT, DATE STRING)");
db.execSQL("create table "+TABLE_NAME1+" (ID INTEGER PRIMARY KEY AUTOINCREMENT, Table1Field TEXT)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME1);
onCreate(db);
}
如果有人能解释为什么我的并行代码如此之慢,那会很棒吗?代码有什么问题,我在这里缺少什么,以及在内存50K +中搜索大片的推荐方法。
答案 0 :(得分:4)
这看起来只是一个简单的错字。问题是你将原来的大切片分成5块(countOfSlices
),并正确启动5个goroutines来搜索每个部分:
for i := 0; i < countOfSlices; i++ {
// Fragments of the array passed on to the search method
go func() { c <- Search(data[(part*i):(part*(i+1))], term) }()
}
这意味着您应该获得 5 结果,但不是。你期望4000-1的结果:
for i := 0; i < part-1; i++ {
select {
case records := <-c:
result = append(result, records...)
case <-timeout:
fmt.Println("timed out!")
return result
}
}
显然,如果你只推出了5个goroutine,每个都提供1个单一结果,你只能期望尽可能多(5)。而且由于你的循环等待了很多(永远不会来),它会超出预期。
将条件更改为:
for i := 0; i < countOfSlices; i++ {
// ...
}
答案 1 :(得分:1)
并发不是并行性。 Go是大规模并发语言,而不是并行。即使使用多核机器,在计算线程中访问共享切片时,也会为CPU之间的数据交换付费。例如,您可以利用第一次匹配时的并发搜索。或者对结果做一些事情(比如打印它们,或写一些Writer,或者排序),而继续搜索。
func Search(data []map[string]string, term string, ch chan map[string]string) {
for _, v := range data {
if v["key"] == term {
ch <- v
}
}
}
func main(){
...
go search(datapart1, term, ch)
go search(datapart2, term, ch)
go search(datapart3, term, ch)
...
for vv := range ch{
fmt.Println(vv) //do something with match concurrently
}
...
}
搜索大切片的推荐方法是保持排序,或制作二叉树。没有魔力。
答案 2 :(得分:0)
有两个问题 - 因为icza注意到你从未完成选择,因为你需要使用countOfSlices,然后你的搜索调用将无法获得你想要的数据,因为你需要在调用go func()之前分配它,所以在外面分配切片并传入。
您可能会发现,与这样简单的数据并行执行此特定工作仍然不是更快(可能在具有大量内核的机器上使用更复杂的数据)这是值得的吗?
确保在测试时尝试交换测试运行的顺序 - 您可能会对结果感到惊讶!也许可以尝试测试包中提供的基准测试工具,它可以为您运行代码很多次并平均结果,这可以帮助您更好地了解扇出是否加快了速度。