我正在为NoSQL数据库编写一个c# SDK,为了处理请求(例如序列化或反序列化,签名),我必须创建很多(小)对象。
我在一个简单的程序中测试了这个新SDK的性能,该程序创建了指定数量的线程,并在每个线程中循环调用api作为playload。对于1个线程,QPS达到6K+,但随着线程数的增加,总体QPS下降而不是增加。为了找出原因,我简化了我的测试程序,并将有效负载减少到一个非常简单的代码中,而不是实际调用我的SDK接口:
for (int i = 0; i < 100000; i ++) {
double a = Math.Pow(3.14, 0.5);
}
性能结果很好:
1线程132 QPS
2个线程261 QPS
4个线程1028 QPS
8线程1826 QPS
但是当我将有效载荷更改为:
for (int i = 0; i < 100000; i ++) {
var c = new string('X', 50);
}
性能如下:
1线程300 QPS
2个线程497 QPS
4个线程596 QPS
8线程518 QPS
如果我在负载中创建一些其他对象,结果将是相同的(不是线性的)。
(以上两种情况均未达到CPU和内存限制)
为什么?我的SDK自然会创建一个对象,有办法解决吗?
我的一些猜想:
- . net框架的内存分配有性能瓶颈,所以当你并行创建对象时,你会受到影响。
- . net框架的GC开始工作并显著降低了性能。
多线程编程的主要规则是您不应该创建多于CPU cores + 1
的线程。这是因为您创建了许多线程,它们都试图同时完成它们的工作,并且您的CPU和操作系统降低了context switching
的性能。这是一个非常耗时的操作——在同一个线程中做两件工作比在两个线程中做一件工作要好。
有很多技术可以提高应用程序的性能,例如,窃取工作,但你应该自己研究,因为这是一个非常广泛的话题。