我在运行多线程模拟时观察到奇怪的行为。在开始时,所有计算线程都被最大化。然后在一些帧之后,一些线程将CPU上的工作负载减少到四分之一,而一个线程保持最大化。在进一步的步骤之后,所有线程再次最大化。我在Windows和Linux中都观察到相同的效果。什么可以导致继续运行相同代码的线程更长时间地获得较低的CPU使用率(四分之一),然后返回其最大值?
模拟在2d域(矩阵)上完成,分为N个条带,其中N等于线程数。在每个帧之后,线程被同步并且交换缓冲区。观察到帧率降低了与最大CPU使用量相同的顺序。线程处理以下代码:
void Sim2d::Test_ModelState::process(uintmax_t framecounter
,const Bufferinfo& buffer_src,Bufferinfo& buffer_dest
,unsigned int offset)
{
auto ptr_dest=buffer_dest.bufferGet<Test_PixelData>();
auto ptr_src=buffer_src.bufferGet<Test_PixelData>();
auto W=buffer_dest.widthGet<Test_PixelData>();
auto h=buffer_dest.heightGet();
auto H=buffer_src.heightGet();
auto F=m_params.feed_rate;
auto ka=m_params.decay_rate;
auto d=m_params.diff_ratio;
auto k_start=offset==0?1:0;
for(uint32_t k=k_start; k<h && k+offset<H-1; ++k)
{
for(uint32_t l=1;l<W-1;++l)
{
auto dyy_u=ptr_src[k+offset+1][l].u - 2*ptr_src[k+offset][l].u
+ ptr_src[k+offset-1][l].u;
auto dxx_u=ptr_src[k+offset][l+1].u - 2*ptr_src[k+offset][l].u
+ ptr_src[k+offset][l-1].u;
auto dyy_v=ptr_src[k+offset+1][l].v - 2*ptr_src[k+offset][l].v
+ ptr_src[k+offset-1][l].v;
auto dxx_v=ptr_src[k+offset][l+1].v - 2*ptr_src[k+offset][l].v
+ ptr_src[k+offset][l-1].v;
auto l_u=dxx_u+dyy_u;
auto l_v=dxx_v+dyy_v;
auto v_u=ptr_src[k+offset][l].u;
auto v_v=ptr_src[k+offset][l].v;
ptr_dest[k][l].u=v_u + 0.06125*( d*l_u - v_u*v_v*v_v + F*(1-v_u) );
ptr_dest[k][l].v=v_v + 0.06125*( l_v + v_u*v_v*v_v - (F+ka)*v_v );
}
ptr_dest[k][0]=ptr_dest[k][1];
ptr_dest[k][W-1]=ptr_dest[k][W-2];
}
if(offset + h == H)
{
for(uint32_t l=0;l<W;++l)
{ptr_dest[h-1][l]=ptr_dest[h-2][l];}
}
if(offset==0)
{
for(uint32_t l=0;l<W;++l)
{ptr_dest[0][l]=ptr_dest[1][l];}
}
}
循环更新分配给当前线程的数据块。该块在矩阵中开始偏移行。将整个矩阵作为输入缓冲区传递的原因是为了支持周期性边界条件(该示例使用Neumann条件)。
两个缓冲区由两个在两个帧之间交换的BufferinfoPair对象管理
namespace Sim2d
{
struct BufferinfoPair
{
BufferinfoPair(Vector::MatrixStorage<ValueType>& matrix_first
,Vector::MatrixStorage<ValueType>& matrix_second
,uint32_t height_block,uint32_t offset);
Bufferinfo first;
Bufferinfo second;
};
inline void swap(BufferinfoPair& a,BufferinfoPair& b)
{
::std::swap(a.first.m_buffer,b.first.m_buffer);
::std::swap(a.second.m_buffer,b.second.m_buffer);
}
}
Sim2d::BufferinfoPair::BufferinfoPair(
Vector::MatrixStorage<ValueType>& matrix_first
,Vector::MatrixStorage<ValueType>& matrix_second
,uint32_t height_block,uint32_t offset):
first
{
matrix_first.rowsGet()
,uint32_t(matrix_first.nColsGet())
,uint32_t(matrix_first.nRowsGet())
}
,second
{
matrix_second.rowsGet()+offset
,uint32_t(matrix_second.nColsGet())
,uint32_t(height_block)
}
{}
Bufferinfo
看起来像
namespace Sim2d
{
class BufferinfoPair;
void swap(BufferinfoPair& a,BufferinfoPair& b);
class Bufferinfo
{
public:
Bufferinfo(ValueType* const* buffer,uint32_t width,uint32_t height)
:m_buffer(buffer),m_width(width),m_height(height)
{}
template<class T>
const T* const* bufferGet() const
{return (const T* const*)m_buffer;}
template<class T>
T* const* bufferGet()
{return (T* const*)m_buffer;}
template<class T>
uint32_t widthGet() const
{return m_width/ (sizeof(T)/sizeof(ValueType));}
uint32_t heightGet() const
{return m_height;}
private:
ValueType* const* m_buffer;
uint32_t m_width;
uint32_t m_height;
friend void swap(BufferinfoPair& a,BufferinfoPair& b);
};
}
最后,每个线程执行的主循环[是一个goto
但我真的不喜欢while(true)
构造,因为它们没有语义含义]:
int Sim2d::DatablockProcessor::run()
{
m_stop=0;
next_frame:
{
start.wait();
if(m_stop)
{return STATUS_OK;}
m_model->process(m_framecounter,m_buffers[0].first,m_buffers[0].second
,m_offset);
swap(m_buffers[0],m_buffers[1]);
ready.set();
goto next_frame;
}
}
m_buffer
被声明为
BufferinfoPair m_buffers[2];
内部DatablockProcessor
数据块区域的计算如下所示
double bh_avg=double(m_buffers.first.nRowsGet())/N;
uint32_t offset=uint32_t(k*bh_avg);
uint32_t height_block=uint32_t((k+1)*bh_avg) - offset;
Sim2d::BufferinfoPair buffers[2]=
{
{m_buffers.first,m_buffers.second,height_block,offset}
,{m_buffers.second,m_buffers.first,height_block,offset}
};
我想到的可能原因是
我排除了原因3,因为如果我尝试其他一些代码(填充白噪声),我也不会遇到同样的问题。
原因4不太可能,因为它一遍又一遍地处理相同的数据。
可以排除原因5,因为初始状态在给定所有线程时都具有非零值,但是当出现零值时,帧速率将丢弃。
可以排除原因6,因为在慢处理期间,大多数像素的渐变中出现小值。那么,除了一个线程之外,更有可能拥有大量的CPU使用率。
排除原因7,因为每个帧应该相同
原因8可能听起来很奇怪,但我发现在域名完全填满后速度会上升。发生这种情况时,像素值随处可见。引入噪声支持这种假设,因为噪声使问题消失。另一方面:这样的测量是否真的可行
观察到的行为最可能的原因是什么?
编辑:
主线程如下所示:
proc_ptr=processors.begin();
while(proc_ptr!=processors.end())
{
proc_ptr->frameNext();
++proc_ptr;
}
while(proc_ptr!=processors.begin())
{
--proc_ptr;
proc_ptr->wait();
}
这段代码是否支持某些线程?我尝试以更随机的顺序开始,没有成功。需要注意的另一件事是更改模型,以便它只吐出一个常量,如
ptr_dest[k][l].u=0; //+ 0.06125*( d*l_u - v_u*v_v*v_v + F*(1-v_u) );
ptr_dest[k][l].v=1; //+ 0.06125*( l_v + v_u*v_v*v_v - (F+ka)*v_v );
使问题消失。我得出结论,从结果帧率来看,双循环还没有被优化出来。
编辑2:
停止模拟线程并从当前状态重新启动它会导致性能不佳,因此我得出结论,像素值会影响调度。