下面有一点长的描述,但这是一个非常棘手的问题.我试图覆盖我们对该问题的了解,以缩小搜索范围.问题更多的是正在进行的调查,而不是基于单一问题的调查,但我认为它也可能有助于其他人.但如果您认为我对下面的某些假设有误,请在评论中添加信息或纠正我.
更新2013年2月19日:我们已经清除了一些问号,我有一个理论,我将在下面更新主要问题.但是还没准备好写一个"已解决"的回复.
2013年4月24日更新:现在生产稳定(虽然我认为这是暂时的),但我认为这是由于两个原因造成的.1)端口增加,2)减少传出(转发)请求的数量.我会在正确的背景下继续这个更新.
我们目前正在我们的生产环境中进行调查,以确定当进行太多传出异步Web服务请求时,我们的IIS Web服务器无法扩展(一个传入请求可能会触发多个传出请求).
CPU只有20%,但我们收到传入请求的HTTP 503错误,许多传出的Web请求得到以下异常:"SocketException:无法执行套接字上的操作,因为系统缺少足够的缓冲区空间或因为队列是"显然,某个地方存在可扩展性瓶颈,我们需要找出它是什么以及是否有可能通过配置来解决它.
应用背景:
我们在Windows 2008 R2 64位操作系统上使用.NET 4.5运行IIS v7.5集成托管管道.我们在IIS中只使用了一个工作进程.硬件略有不同,但用于检查错误的机器是Intel Xeon 8核心(16个超线程).
我们使用异步和同步Web请求.那些异步的是使用新的.NET异步支持来使每个传入请求在应用程序中向持久化TCP连接上的其他服务器发出多个HTTP请求(保持活动状态).同步请求执行时间低0-32 ms(由于线程上下文切换而发生更长时间).对于异步请求,在请求中止之前,执行时间最长可达120毫秒.
通常,每台服务器最多可以提供约1000个传入请求.当问题开始出现时,传出请求是~300个请求/秒,最多约600个请求/秒.仅在传出异步时才会出现问题.请求在服务器上启用,我们超过一定级别的传出请求(~600 req./s).
解决问题的可能方法:
在这个问题上搜索互联网揭示了许多可能的候选解决方案.但是,它们非常依赖于.NET,IIS和操作系统的版本,所以在我们的上下文中找到一些东西需要时间(anno 2013).
下面列出了解决方案候选人以及我们迄今为止在配置背景方面得出的结论.到目前为止,我已将检测到的问题区域分类为以下主要类别:
传出的异步请求异常消息确实指示某个缓冲区队列已被填满.但它没有说哪个队列/缓冲区.通过IIS论坛(以及那里引用的博客文章),我已经能够在下面标记为AF的请求管道中区分4个可能的6个(或更多)不同类型的队列.
虽然应该说明所有下面定义的队列,我们可以肯定地看到1.B)ThreadPool性能计数器Requests Queued在有问题的负载期间变得非常满.因此问题的原因可能是.NET级别而不是低于此级别(CF).
我们使用.NET框架类WebClient发出异步调用(异步支持),而不是我们遇到的HttpClient具有相同的问题,但具有低得多的req/s阈值.我们不知道.NET Framework实现是否隐藏任何内部队列或不在线程池之上.我们认为情况并非如此.
线程池充当自然队列,因为.NET线程(默认)调度程序正从线程池中挑选线程以执行.
性能计数器:[ASP.NET v4.0.30319].[请求排队].
配置可能性:
如果线程池已满,请求将开始堆积在此本机(未管理)队列中.
性能计数器: [ASP.NET v4.0.30319].[本机队列中的请求]
配置可能性: ????
此队列与上面的1.C)队列不同.这是我所说的解释"HTTP.sys内核队列本质上是一个完成端口,用户模式(IIS)从内核模式(HTTP.sys)接收请求.它有一个队列限制,当超过该限制时,您将收到503状态代码.HTTPErr日志还将通过记录503状态和QueueFull来指示这发生了.
性能计数器: …
我试图通过测试来测量DDR3内存数据传输速率.根据CPU规格.最大理论带宽为51.2 GB/s.这应该是四个通道的组合带宽,意味着12.8 GB /通道.然而,这是一个理论上的限制,我很好奇如何进一步提高这篇文章的实际限制.在下面描述的测试场景中,我实现了~14 GB/s的数据传输速率,我认为在杀死CPU L1,L2和L3高速缓存的大部分吞吐量增强时可能是近似的.
更新2014年第20/3期: 这种杀死L1-L3缓存的假设是错误的.内存控制器的硬件预取将分析数据访问模式,并且由于它是顺序的,因此它将具有将数据预取到CPU高速缓存中的简单任务.
具体问题在底部,但主要是我感兴趣的a)对导致该结果的假设的验证,以及b)是否有更好的方法在.NET中测量内存带宽.
我在.NET上用C#构建了一个测试作为入门者.虽然.NET从内存分配的角度来看并不理想,但我认为这对于这个测试是可行的(如果你不同意,请告诉我,为什么).测试是分配一个int64数组并用整数填充它.该数组应该在内存中对齐数据.然后,我使用与机器上的核心一样多的线程循环此数组,并从数组中读取int64值并将其设置为测试类中的本地公共字段.由于结果字段是公共的,我应该避免编译器优化循环中的东西.此外,这可能是一个弱假设,我认为结果保留在寄存器中,直到它再次被写入才被写入存储器.在每次读取数组中的元素之间,我在数组中使用10,100和1000的变量Step偏移量,以便无法在同一缓存块(64字节)中获取许多引用.
从数组中读取Int64应该意味着读取8个字节,然后读取实际值8个字节.由于数据是从64字节高速缓存行的内存中提取的,因此每次在循环中从RAM中读取的每个64字节应该对应于读取数据不在任何CPU高速缓存中.
以下是我初始化数据数组的方法:
_longArray = new long[Config.NbrOfCores][];
for (int threadId = 0; threadId < Config.NbrOfCores; threadId++)
{
_longArray[threadId] = new long[Config.NmbrOfRequests];
for (int i = 0; i < Config.NmbrOfRequests; i++)
_longArray[threadId][i] = i;
}
Run Code Online (Sandbox Code Playgroud)
这是实际的测试:
GC.Collect();
timer.Start();
Parallel.For(0, Config.NbrOfCores, threadId =>
{
var intArrayPerThread = _longArray[threadId];
for (int redo = 0; redo < Config.NbrOfRedos; redo++)
for (long i = 0; i < Config.NmbrOfRequests; i += Config.Step) …Run Code Online (Sandbox Code Playgroud) 我有一个布尔表达式,我已设法在SSE2中实现.现在我想尝试在AVX中实现它,在并行性增加中利用额外的因子2(从128位SIMD类型到256).但是,AVX不支持整数操作(AVX2可以使用,但我正在使用Sandy Bridge处理器,因此它目前不是一个选项).但是,由于存在用于按位运算的AVX内在函数.我想我可以尝试将我的整数类型转换为浮点类型并查看它是否有效.
第一次测试是成功的:
__m256 ones = _mm256_set_ps(1,1,1,1,1,1,1,1);
__m256 twos = _mm256_set_ps(2,2,2,2,2,2,2,2);
__m256 result = _mm256_and_ps(ones, twos);
Run Code Online (Sandbox Code Playgroud)
我正在按照自己的意愿来训练所有的0.模拟AND'ing两个而不是我得到2的结果.但是当相应地尝试11 XOR 4时:
__m256 elevens = _mm256_set_ps(11,11,11,11,11,11,11,11);
__m256 fours = _mm256_set_ps(4,4,4,4,4,4,4,4);
__m256 result2 = _mm256_xor_ps(elevens, fours);
Run Code Online (Sandbox Code Playgroud)
结果是6.46e-46(即接近0)而不是15.模拟地做11 OR 4给出的值为22而不是15应该是.我不明白为什么会这样.这是一个我错过的bug还是一些配置?
我实际上期待我使用float的假设,好像它们是整数不起作用,因为初始化为浮点值的整数可能实际上不是精确值而是近似值.但即便如此,我对我得到的结果感到惊讶.
有没有人有这个问题的解决方案或者我必须升级我的CPU才能获得AVX2支持吗?
我在使用布尔类型时遇到了一些问题,并在C#和C之间在结构中来回编组.我在C中非常生疏,但希望在那部分没有任何重大错误.
据我所知,.NET Boolean和C#bool类型长4个字节,而C类型bool只有1个字节.出于内存占用的原因,我没有在C代码中使用定义的BOOL 4字节版本.
这是一些简单的测试代码,希望能够清楚地表达我的问题:
C代码:
typedef struct
{
double SomeDouble1;
double SomeDouble2;
int SomeInteger;
bool SomeBool1;
bool SomeBool2;
} TestStruct;
extern "C" __declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs);
__declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs)
{
return structs;
}
Run Code Online (Sandbox Code Playgroud)
我使用以下定义在C#中调用此代码:
[StructLayout(LayoutKind.Explicit)]
public struct TestStruct
{
[FieldOffset(0)]
public double SomeDouble1;
[FieldOffset(8)]
public double SomeDouble2;
[FieldOffset(16)]
public int SomeInteger;
[FieldOffset(17), MarshalAs(UnmanagedType.I1)]
public bool SomeBool1;
[FieldOffset(18), MarshalAs(UnmanagedType.I1)]
public bool SomeBool2;
};
[DllImport("Front.dll", CharSet = CharSet.Auto, SetLastError = …Run Code Online (Sandbox Code Playgroud)