2008-8-9 version 1.0
Intel实现的Simultaneous Multi-Threading技术:一个物理核模拟多个逻辑核;
原理:定义线程只需要体系结构状态信息即可,包括寄存器、cache,通过复制这些状态信息创建多个逻辑处理器,而执行资源(计算资源?执行资源的进一步细分?)被这些逻辑处理器共享;OS可以将多个线程调度到CPU上,但执行资源只有一个,所以微体系结构需要确定线程切换。
也就是说,超线程只是在CPU上保存了两个线程的状态信息,微体系自己确定其调度,以供OS同时调度两个线程到CPU上执行?
HT通过减少CPU闲置时间来提高系统吞吐量,一般约30%;
加速比:可加速部分,程序多核加速能力受限于不可加速部分的比例;
分解:进行任务划分,并确定任务间的依赖关系;
l 任务分解:不同程序行为使用不同的线程,如GUI的用户界面线程与工作线程;
l 数据分解:多个线程对不同的数据块执行相同的操作,如用于音视频处理;
l 数据流分解:一个线程的输出作为另一个线程的输入;
n 必须避免等待生产者线程结果的过程中,出现消费者线程必须闲置的情况;
n 生产者与消费者之间的交互调度方案;
n 所有线程之间的负载平衡;
可扩展性:性能能否随CPU数量线性增长;
栅栏:用于多核、多处理系统环境中保证存储操作的一致性;
栅障:使线程在控制流的某个逻辑点上集合;
l 创建退出:CreateThread、ExitThread、_beginthreadex、_endthreadex:ExitThread会使得线程在清理自动变量、调用C++释函前退出;CreateThread不会执行C运行时数据块的pre-thread initialization,_beginthreadex用于解决该问题;
l 暂停恢复:SuspendThread、ResumeThread、TerminateThread,挂起操作可能是危险的,如线程不会释放已经占用的锁,而TerminateThread会使得已经占用的锁无法再释放;
l 原子操作:InterlockedIncrement等一系列;InitializeSListHead、InterlockedPushEntrySlist等对链表的原子操作;
l 线程池:QueueUserWorkItem;
l 优先级:SetProcessPriorityBoost、SetThreadPriorityBoost;
l 处理器亲和(Affinity):SetThreadAffinityMask、SetProcessAffinityMask,强制处理器绑定,SetThreadIdealProcessor建议亲和;通过GetSystemInfo获得CPU信息;
l 纤程fiber:提供用户级的线程支持,ConvertThreadToFiber将当前线程转化为主纤程,SwitchToFiber、DeleteFiber、GetCurrentFiber;
l 线程命名:使用自定义的函数SetThreadName,仅用于调试器或Windbg;在MSDN上找到的函数实现如下:
//
// Usage: SetThreadName (-1, "MainThread");
//
#define MS_VC_EXCEPTION 0x406D1388
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = szThreadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
{
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
}
__except(EXCEPTION_CONTINUE_EXECUTION)
{
}
}
dwThreadID设置为-1表示当前线程;
VS2005的tracepoint——执行到该点时执行某些操作,如调用vs宏;
Windows中,不能在单个进程空间中混用VC提供的静态库和动态库,特别注意exe与用到的dll应使用相同的C库;
l 创建通告,[New Thread xxx]
l 所有线程信息,info threads
l 特定线程上的断点,break linespec thread threadnum [if a > 3]
l 切换线程,thread threadnum,threadnum由info threads返回;
l 向一组线程发消息:thread apply all bt,向所有线程发bt消息;thread apply 1 2 bt,向线程1、2发消息;
1, 检查程序性能:
OS空闲循环的原因:负载不均衡、被阻塞的同步、过多的串行区、cache未中次数过多、伪共享;
2, 线程过多
控制线程数为核心数或外层cache数,如HT只有一个cache,这样在线程需要较多cache而互相争夺cache时,应将线程数限制为cache数;
将计算线程与I/O线程分离,计算线程大多数时间是可运行线程,I/O线程多数时间在等待外部事件;
3, 死锁的处理
a) 使用数据复制避免使用锁;
b) 锁获取顺序,在获得B前必须先获得A;
c) 尝试获取,获得A后尝试获得B,如果失败则释放A;
4, 锁竞争激烈
a) 不使用锁
b) 细粒度锁
c) 读写锁
5, 非阻塞算法,基于原子操作
一个有趣的算法
long x_old, x_new, x_was;
do{
x_old = x;
x_new = fun(x_old);
x_was = interlockedcompareexchange(&x, x_new, x_old);
}while(x_was!=x_old)
1) 该算法可能导致ABA问题:x从A被改成B,后又改成A;
2) 非阻塞算法可能导致cache行乒乓现象
3) Free操作是一个复杂的问题;