Golang Go语言中如何提升 strings.ToLower 的性能

发布于 1周前 作者 zlyuanteng 来自 Go语言

需要进行大小写不区别的子串检测(含中英文)

同样的代码比 Python 还慢点

func checkAgainstKeywords(s string) bool {
	s = strings.ToLower(s)
	for _, keyword := range keywords {
		if !strings.Contains(s, keyword){
			return false
		}
	}
	return true
}
def multi_search(keywords, string):
    lowerstring=string.lower()
    for keyword in keywords:
        if keyword not in lowerstring:
            return False
    return True

Golang Go语言中如何提升 strings.ToLower 的性能

更多关于Golang Go语言中如何提升 strings.ToLower 的性能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

17 回复

Go 的字符串是 utf-8 的,所以要搞清楚你处理的是 unicode 还是 bytes 。如果是后者,可以这样:

func toLower(s string) string {
b := make([]byte, len(s))
for i := range b {
c := s[i]
if c >= ‘A’ && c <= ‘Z’ { c += ‘a’ - ‘A’ }
b[i] = c
}
return string(b)
}

更多关于Golang Go语言中如何提升 strings.ToLower 的性能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


…这你都压榨。。 提升硬件把

想把一个 string 的大写英文字符转为小写

根据 char 的值判断应该会比 contains/in 之类的快一点吧

如果真的在乎这点性能,建议重新实现函数,或者换语言

明摆着应该用自动机

这个性能真是在 ToLower 而不是 Contains ?

Keyword 数量太多的话建议使用 Trie 树

“需要进行大小写不区别的子串检测”

瓶颈不在与 ToLower ,而在于子串识别的算法。子串识别有两个很基本的算法:

Multiple text with single pattern: KMP Algorithm
Multiple text with multiple pattern: Aho-Corasick algorithm ( AC 自动机)

https://golang.org/src/strings/strings_amd64.go?s=521:550#L3

这里可以看到 go 实现的是 Rabin-Karp algorithm 。假设文本长度为 n ,单个关键字长度为 m ,那么 Rabin-Karp 的方法最坏复杂度是 O(nm),不过一般情况下是 O(n+m)。假设你这里有 w 个关键字,那么总的复杂度是 O(w*(n+m))。

如果关键字多并且文本比较长的话,改用 AC 自动机,复杂度就降到了 O(n + w*m)

https://hg.python.org/cpython/file/2.7/Objects/stringlib/fastsearch.h

Python 用了一个跑的比较快的匹配算法,所以表现起来比 golang 好一些

谢谢回复,我判断出是 ToLower 的原因是我把 ToLower 去掉后,总执行时间从 200 多 ms 下降到十几 ms

函数调用次数 200+k

原来是这样。不过这也不一定是 ToLower 的问题,也有可能是在原串上做匹配触发了某些条件使得字符串匹配变快了。

刚刚看了下 Strings.ToLower 的代码,发现可能确实 ToLower 会存在性能问题。他是直接用了个 map ,然后 mapper function 还要往下递归两三层。在这种情况下,编译器优化再厉害也是不如手写个 for 来得快的。

pprof 了一下

你们一提到算法我写野生的就插不上话。

学了就可以讨论了

lz 该用自动机啊!楼上的分析得很不错了。
而且直接返回 strings.Contains 不就好了?还 if 一下…
估计你还不知道:
1. string 是会复制的,多用 bytes
2. haystack 小于 cpu 的位长时对比只需要一个 cpu op



用 if 判断是因为多个 substring 都要符合

[]byte 貌似不能解决中文处理问题,而[]rune 没有办法 split 或者像[]byte 一样 ReadBytes

在Go语言中,strings.ToLower 函数用于将字符串中的所有字符转换为小写。尽管这个函数已经相当高效,但在某些高性能要求的场景下,仍有一些优化策略可以考虑:

  1. 避免不必要的调用:最直接的方法是减少 strings.ToLower 的调用次数。例如,如果某个字符串需要多次使用其小写形式,可以将其存储在一个变量中,避免重复转换。

  2. 使用字节切片直接操作:对于非常大的字符串,直接操作字节切片可能比使用 strings.ToLower 更高效。你可以手动遍历字符串的字节切片,根据ASCII码表进行小写转换。这种方法虽然复杂,但在处理极大数据量时可能带来性能提升。

  3. 并发处理:如果字符串处理是CPU密集型任务且数据量很大,可以考虑使用Go的并发特性(如goroutines和channels)来并行处理字符串转换,从而利用多核CPU的优势。

  4. 性能分析与基准测试:在尝试任何优化之前,使用Go的性能分析工具(如pprof)来确定性能瓶颈。同时,通过基准测试(使用testing包中的Benchmark函数)来验证优化效果。

  5. 考虑第三方库:有些第三方库可能提供了针对特定场景的优化版本,尽管这通常不是首选,但在某些情况下可能值得一试。

总之,优化 strings.ToLower 的性能需要根据具体应用场景来定制策略,通常需要从减少调用次数、优化数据处理方式以及利用并发等方面入手。

回到顶部