❤️ 从125ms到11ms,记一次关键字检测过滤服务的优化 -python and Pythonnet
接上文:《高效的的关键字查找和检测(哈希表和Trie前缀树和FastCheck)在实际使用中的性能》
动态语言是很慢的,它更多的是为了提高开发效率,这里的关键字过滤算法在生产环境中用原生python达到125ms 2千万字/每秒已经够用了。那么是不是可以适当的优化再快一点?
Cython,没事cython一下,带你飞
cython能把大部分python代码改为静态的c实现,甚至你可以混合c和python一起写。不过现实是大部分的pythoner如果碰到性能问题要么用go要么就是用csharp,很少去写cython。下面是用cython编译后的测试结果:
提升39%,马马虎虎,也可能跟Tool.Good源码的实现有关。此时文本的处理量已经到了3400万/每秒,用是够用了,那么能不能更快一点?
C# 是时候展现你的实力了
Tool.Good作者明显是一位纯粹的csharpner, 既然已经实现了3亿效率的代码为什么不拿来用呢。这里作为对比,我先写了一个参照的测试程序,测试环境与前文相同。代码如下
using System;
using ToolGood.Words;
namespace KeywordTest
{
class Program
{
static void Main(string[] args)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
string post = System.IO.File.ReadAllText(@"D:\Projects\Opensource\ToolGood.Words\csharp\KeywordTest\sample_post");
string[] spams = System.IO.File.ReadAllLines(@"D:\Projects\Opensource\ToolGood.Words\csharp\KeywordTest\SpamWordsCN.min.txt");
StringSearch iwords = new StringSearch();
iwords.SetKeywords(spams);
stopwatch.Start();
for (var i = 0; i < 500; i++)
{ var f = iwords.FindFirst(post); }
stopwatch.Stop();
var s = stopwatch.ElapsedMilliseconds;
Console.WriteLine("测试用时(ms):" + s);
}
}
}
Csharp性能都这样了更别说C了,那么为什么能比cython快那么多呢。这里分两部分原因,一部分性能浪费在python和c的交互上,另一部分在cython编译时的类型推断和生成的代码优化不够,大量使用的还是pure python。咱不纠结这个,手头有这么快的csharp不用简直浪费,一脚踢走cython,黏上csharp,让它飞的更高~
Pythonnet 闪亮登场
这可是个好东西,.net基金会项目,官方身份,神秘可靠。有了它使得python和.net交互变得简单。这里需要一提的是目前稳定版2.5.2还只支持.net 4.0,至于.net core 3和.net5需要手动安装master分支上的3.0.0dev,实现方式 也有所不同,下面是代码:
def test2():
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt = get_coreclr("./runtimeconfig.json")
set_runtime(rt)
import clr
clr.AddReference('ToolGood.Words')
import clr
from System import String
from ToolGood.Words import StringSearch
stringSearch=StringSearch()
with open('./sample_post',encoding='utf-8') as f:
test_post = f.read()
from System.Collections.Generic import List
spam_words=List[String]()
with open('././SpamWordsCN.min.txt',encoding='utf-8') as f:
for line in [line.rstrip() for line in f]:
spam_words.Add(String(line))
stringSearch.SetKeywords(spam_words)
import time
start = time.time()
times = 500
while times > 0:
f = stringSearch.FindFirst(test_post)
times -= 1
end = time.time()
print('程序运行时间:%s毫秒' % ((end - start) * 1000))
{
"runtimeOptions": {
"tfm": "net5.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "5.0.2"
}
}
}
事实上如果增加压力还能提高性能,因为交互部分压力越大性价比越高。此时的处理速度已经到了2.5亿/每秒。那么能不能再快点?烦不烦,其实也不烦,生产中怎么可能不并行呢,动不动八核十几核的,线程再来double一下,妥妥的几十亿。