Перейти строки. Содержит () в 2 раза медленнее, чем Python3?
Я конвертирую сканер текстовых шаблонов из Python3 в Go1.10, но удивляюсь, что он на самом деле в 2 раза медленнее. После профилирования виновник strings.Contains()
, Смотрите простые тесты ниже. Я что-то пропустил? Не могли бы вы порекомендовать более быстрый алгоритм поиска по шаблону для Go, который будет работать лучше в этом случае? Меня не беспокоит время запуска, тот же шаблон будет использоваться для сканирования миллионов файлов.
Тест Py3:
import time
import re
RUNS = 10000
if __name__ == '__main__':
with open('data.php') as fh:
testString = fh.read()
def do():
return "576ad4f370014dfb1d0f17b0e6855f22" in testString
start = time.time()
for i in range(RUNS):
_ = do()
duration = time.time() - start
print("Python: %.2fs" % duration)
Тест Go1.10:
package main
import (
"fmt"
"io/ioutil"
"log"
"strings"
"time"
)
const (
runs = 10000
)
func main() {
fname := "data.php"
testdata := readFile(fname)
needle := "576ad4f370014dfb1d0f17b0e6855f22"
start := time.Now()
for i := 0; i < runs; i++ {
_ = strings.Contains(testdata, needle)
}
duration := time.Now().Sub(start)
fmt.Printf("Go: %.2fs\n", duration.Seconds())
}
func readFile(fname string) string {
data, err := ioutil.ReadFile(fname)
if err != nil {
log.Fatal(err)
}
return string(data)
}
data.php
файл размером 528 КБ, который можно найти здесь.
Выход:
Go: 1.98s
Python: 0.84s
2 ответа
Я сделал больше сравнительного анализа с различными реализациями поиска строк, которые я нашел в Википедии, такими как:
- https://github.com/cloudflare/ahocorasick
- https://github.com/cubicdaiya/bms
- https://github.com/kkdai/kmp
- https://github.com/paddie/gokmp
- https://github.com/hillu/go-yara (кажется, Yara внедряет Aho & Corasick под капот).
Результаты тестов ( код здесь):
BenchmarkStringsContains-4 10000 208055 ns/op
BenchmarkBMSSearch-4 1000 1856732 ns/op
BenchmarkPaddieKMP-4 2000 1069495 ns/op
BenchmarkKkdaiKMP-4 1000 1440147 ns/op
BenchmarkAhocorasick-4 2000 935885 ns/op
BenchmarkYara-4 1000 1237887 ns/op
Затем я сравнил свой практический пример с тестированием около 1100 сигнатур (100 регулярных выражений, 1000 литералов) с файлом несоответствия размером 500 КБ, как для нативного (strings.Contains
а также regexp
) и реализации Yara на основе C:
BenchmarkScanNative-4 2 824328504 ns/op
BenchmarkScanYara-4 300 5338861 ns/op
Хотя C-вызовы в Go предположительно дороги, в этих "тяжелых" операциях прибыль замечательна. Боковое наблюдение: процессору Yara требуется в 5 раз больше процессорного времени, чтобы соответствовать 1100 сигнатурам вместо 1.
Почему Python 3 (24,79 с) в 4,5 раза медленнее, чем Go (5,47 с)? Какие результаты вы получаете?
Python:
$ cat contains.py
import time
import re
RUNS = 10000
if __name__ == '__main__':
# The Complete Works of William Shakespeare by William Shakespeare
# http://www.gutenberg.org/files/100/100-0.txt
file = '/home/peter/shakespeare.100-0.txt' # 'data.php'
with open(file) as fh:
testString = fh.read()
def do():
return "Means to immure herself and not be seen." in testString
start = time.time()
for i in range(RUNS):
_ = do()
duration = time.time() - start
print("Python: %.2fs" % duration)
print(do())
$ python3 --version
Python 3.6.5
$ python3 contains.py
Python: 24.79s
True
$
Go:
$ cat contains.go
package main
import (
"fmt"
"io/ioutil"
"log"
"strings"
"time"
)
const (
runs = 10000
)
func main() {
// The Complete Works of William Shakespeare by William Shakespeare
// http://www.gutenberg.org/files/100/100-0.txt
fname := `/home/peter/shakespeare.100-0.txt` // "data.php"
testdata := readFile(fname)
needle := "Means to immure herself and not be seen."
start := time.Now()
for i := 0; i < runs; i++ {
_ = strings.Contains(testdata, needle)
}
duration := time.Now().Sub(start)
fmt.Printf("Go: %.2fs\n", duration.Seconds())
fmt.Println(strings.Contains(testdata, needle))
fmt.Println(strings.Index(testdata, needle))
}
func readFile(fname string) string {
data, err := ioutil.ReadFile(fname)
if err != nil {
log.Fatal(err)
}
return string(data)
}
$ go version
go version devel +5332b5e75a Tue Jul 31 15:44:37 2018 +0000 linux/amd64
$ go run contains.go
Go: 5.47s
true
5837178
$