Win32 Lock Benchmarking

本文通过对比测试Win32环境下的Mutex和CriticalSection,发现CriticalSection在大多数情况下速度更快,尤其是在循环内加锁的情况下。测试结果显示,不加锁的性能优于Win32 Mutex,而Win32CriticalSection在某些场景下接近不加锁的性能。结论指出,在Windows下,CriticalSection通常是更好的选择。

简介

测试Win32下Mutex以及CriticalSection的效率

方法

创建两个线程,同时在for循环里对一个全局变量Var进行修改,并测量耗时。

代码

#pragma once
#include<Windows.h>
#include<cstdio>
#include<ctime>
#include<mutex>
namespace Test {
	static int Var = 0;
	static int LoopCnt = 10000;
	using ThreadFunc = DWORD(*)(LPVOID);
	//static auto milliseconds_now = clock;
	long long milliseconds_now() {
		static LARGE_INTEGER s_frequency;
		static BOOL s_use_qpc = QueryPerformanceFrequency(&s_frequency);
		if (s_use_qpc) {
			LARGE_INTEGER now;
			QueryPerformanceCounter(&now);
			return (1000LL * now.QuadPart) / s_frequency.QuadPart;
		}
		else {
			return GetTickCount();
		}
	}
	static HANDLE GenThread(ThreadFunc Func,
		LPDWORD ThreadID, int* Inc) {
		return CreateThread(
			NULL,                   // default security attributes
			0,                      // use default stack size  
			Func,					// thread function name
			Inc,          // argument to thread function 
			0,                      // use default creation flags 
			ThreadID);   // returns the thread identifier 
	}
	static DWORD AddFunc_NoSync(LPVOID Param) {
		int Inc = *(int*)Param;
		//int Inc = 1;
		for (int i = 0; i < LoopCnt; i++) {
			Var += Inc;
		}
		return 0;
	}
	static HANDLE ghMutex;
	static DWORD AddFunc_WinMutex(LPVOID Param) {
		int Inc = *(int*)Param;
		for (int i = 0; i < LoopCnt; i++) {
			auto dwWaitResult = WaitForSingleObject(
				ghMutex,    // handle to mutex
				INFINITE);  // no time-out interval
			Var += Inc;
			ReleaseMutex(ghMutex);
		}
		return 0;
	}
	static CRITICAL_SECTION CriticalSection;
	static DWORD AddFunc_WinCritical(LPVOID Param) {
		int Inc = *(int*)Param;
		for (int i = 0; i < LoopCnt; i++) {
			EnterCriticalSection(&CriticalSection);
			Var += Inc;
			LeaveCriticalSection(&CriticalSection);
		}
		return 0;
	}
	static std::mutex StdMutex;
	static DWORD AddFunc_StdMutex(LPVOID Param) {
		int Inc = *(int*)Param;
		for (int i = 0; i < LoopCnt; i++) {
			StdMutex.lock();
			Var += Inc;
			StdMutex.unlock();
		}
		return 0;
	}
	
	static void StartCnt(ThreadFunc Func) {
		DWORD ThreadID;
		int X = 1;
		auto ThreadHandle = GenThread(Func, &ThreadID, &X);
		auto ThreadHandle2 = GenThread(Func, &ThreadID, &X);
		WaitForSingleObject(ThreadHandle, INFINITE);
		WaitForSingleObject(ThreadHandle2, INFINITE);
	}

	static long long NoSyncCnt() {
		auto Beg = milliseconds_now();
		StartCnt(AddFunc_NoSync);
		auto End = milliseconds_now();
		return (End - Beg);
	}
	static long long WinMutexCnt() {
		ghMutex = CreateMutex(
			NULL,              // default security attributes
			FALSE,             // initially not owned
			NULL);             // unnamed mutex
		auto Beg = milliseconds_now();
		StartCnt(AddFunc_WinMutex);
		auto End = milliseconds_now();
		CloseHandle(ghMutex);
		return (End - Beg);
	}
	static long long WinCriticalCnt() {
		InitializeCriticalSection(&CriticalSection);
		auto Beg = milliseconds_now();
		StartCnt(AddFunc_WinCritical);
		auto End = milliseconds_now();
		DeleteCriticalSection(&CriticalSection);
		return (End - Beg);
	}
	static long long StdMutexCnt() {
		auto Beg = milliseconds_now();
		StartCnt(AddFunc_StdMutex);
		auto End = milliseconds_now();
		return (End - Beg);
	}
	void TestNoSync() {
		Var = 0;
		auto Elapse = NoSyncCnt();
		printf("No Sync\t\t: Var = %9d, Elapse = %4lld ms\n", Var, Elapse);
	}
	void TestWinMutex() {
		Var = 0;
		auto Elapse = WinMutexCnt();
		printf("Win Mutex\t: Var = %9d, Elapse = %4lld ms\n", Var, Elapse);
	}
	void TestWinCritical() {
		Var = 0;
		auto Elapse = WinCriticalCnt();
		printf("Win Ctitical\t: Var = %9d, Elapse = %4lld ms\n", Var, Elapse);
	}
	void TestStdMutex() {
		Var = 0;
		auto Elapse = StdMutexCnt();
		printf("Std Mutex\t: Var = %9d, Elapse = %4lld ms\n", Var, Elapse);
	}
	double AvgElapse(long long(*CntFunc)()) {
		int Times = 100;
		long long Elapse = 0;
		for(int i = 0; i < Times; i++){
			Elapse += CntFunc();
		}
		return ((double)Elapse) / Times;
	}
	static void LockBenchmark(){
		//{
		//	printf("### No Sync ###\n");
		//	auto Elapse = AvgElapse(NoSyncCnt);
		//	printf("No Sync\t\t: Var = %9d, Elapse = %f ms\n", Var, Elapse);
		//}
		//{
		//	printf("### Win Mutex ###\n");
		//	auto Elapse = AvgElapse(WinMutexCnt);
		//	printf("Win Mutex\t: Var = %9d, Elapse = %f ms\n", Var, Elapse);
		//}
		//{
		//	printf("### Std Mutex ###\n");
		//	auto Elapse = AvgElapse(StdMutexCnt);
		//	printf("Std Mutex\t: Var = %9d, Elapse = %f ms\n", Var, Elapse);
		//}
		{
			printf("### Win Critical ###\n");
			auto Elapse = AvgElapse(WinCriticalCnt);
			printf("Win Critical\t: Var = %9d, Elapse = %f ms\n", Var, Elapse);
		}
	}
}

// LoopCnt = 10000000, Times = 100, 2 thread, Lock Outer
// No Sync		: 44.37 ms 
// Win Mutex	: 27.30 ms
// Std Mutex	: 27.31 ms
// Win Critical	: 26.80 ms

// LoopCnt = 10000, Times = 100, 2 thread, Lock Inner
// No Sync		:  0.12 ms 
// Win Mutex	: 45.83 ms
// Std Mutex	:  2.53 ms
// Win Critical	:  0.68 ms

结果

测试一:在for循环之外加锁,即

static DWORD AddFunc_WinCritical(LPVOID Param) {
	int Inc = *(int*)Param;
	EnterCriticalSection(&CriticalSection);
	for (int i = 0; i < LoopCnt; i++) {
		Var += Inc;
	}
	LeaveCriticalSection(&CriticalSection);
	return 0;
}

条件:LoopCnt = 10000000, Times = 100, 2 thread, Lock Outer

Avg Elapse/ms
不加锁44.37
Win32 Mutex27.30
Std Mutex27.31
Win32 CriticalSection26.80

测试二:在for循环之内加锁,即:

static DWORD AddFunc_WinCritical(LPVOID Param) {
	int Inc = *(int*)Param;
	for (int i = 0; i < LoopCnt; i++) {
		EnterCriticalSection(&CriticalSection);
		Var += Inc;
		LeaveCriticalSection(&CriticalSection);
	}
	return 0;
}

条件:LoopCnt = 10000, Times = 100, 2 thread, Lock Inner

Avg Elapse/ms
不加锁0.12
Win32 Mutex45.83
Std Mutex2.53
Win32 CriticalSection0.68

Note: 不加锁的情况下Var的值是没有同步的,也就是不一定正确的。

结论

测试一

T ( W i n 32 C r i t i c a l S e c t i o n ) < T ( W i n 32 M u t e x ) < T ( S t d M u t e x ) < T ( N o L o c k ) T(Win32 CriticalSection)<T(Win32 Mutex)<T(StdMutex)<T(NoLock) T(Win32CriticalSection)<T(Win32Mutex)<T(StdMutex)<T(NoLock)

之前看到一篇文章说CriticalSection不用切换到内核态,而Mutex需要,因此CriticalSection会比较快,在这里被验证了。

不加锁是最慢的,这很奇怪,有篇文章有个解释,虽然是在linux上的,但在windows上应该有这个可能性。看完文章,我的理解是操作系统有用户态和内核态之间的切换进程上下文切换这两种切换,加了锁之后进程就不会被切换(优先级高?),这带来的速度增益超过了一次用户态和内核态之间的切换的开销,所以速度反而变快了。这是个需要验证的观点。

测试二

T ( N o L o c k ) < T ( W i n 32 C r i t i c a l S e c t i o n ) < T ( S t d M u t e x ) < < T ( W i n 32 M u t e x ) T(NoLock)<T(Win32 CriticalSection)<T(StdMutex)<<T(Win32 Mutex) T(NoLock)<T(Win32CriticalSection)<T(StdMutex)<<T(Win32Mutex)

测试二与测试一的结果在大小关系方面区别较大。首先测试二的LoopCnt是10000,远小于测试一,这是因为在循环内加锁对于Win32Mutex来说非常慢,因此减小次数。两次测试的数据不能直接比较,要比较的是大小关系。

在测试一中,CriticalSection和Mutex差异不大,但在测试二里明显有了差异,这是因为我们的锁是加在循环里面(导致使用Mutex时频繁地切换内核态和用户态?),而CriticalSection则不用切换。

在测试一中,Win32Mutex和StdMutex相差不大,但在测试二里差异巨大,猜测标准库的Mutex做了优化?或者Win32下的Mutex有更高效的用法?

在测试二中,Win32Mutex远远慢于不加锁的情况,说明如果频繁切换内核-用户态对性能的影响很大。

总结

不考虑跨平台的话,在windows下,需要加锁时,CriticalSection优于Mutex。

TODO

这里线程数量只有2个,比较简单,也许可以增加到几十个来看看结果是否一样。

内容概要:本文围绕虚拟电厂与电动汽车之间的互动关系,采用主从博弈理论构建了二者间的优化决策模型,并引入条件风险价值(CVaR)来衡量和管理电力系统中由可再生能源出力不确定性及电动汽车充放电行为带来的风险。通过Matlab代码实现该模型,旨在优化虚拟电厂的调度策略,在保障系统经济性的同时提升其对不确定因素的鲁棒性。研究涵盖了模型构建、算法设计与仿真验证全过程,重点分析了不同风险偏好下虚拟电厂与电动汽车的博弈均衡结果及其对系统运行的影响,深入探讨了主从博弈框架下的决策机制与CVaR在电力金融风险量化中的应用。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事能源互联网、电力市场、电动汽车调度、风险管理等相关领域研究的研究生或科研人员。; 使用场景及目标:①研究虚拟电厂如何协调管理大量分布式能源与电动汽车资源;②探讨主从博弈在电力市场中的建模方法与求解技巧;③利用CVaR工具量化并控制电力系统运行中的金融或运行风险;④通过Matlab实现复杂优化模型并进行仿真分析。; 阅读建议:此资源结合了博弈论、风险管理和电力系统优化等多学科知识,建议读者在学习过程中重点关注模型假设的合理性、CVaR的应用逻辑以及Matlab代码的实现细节,宜配合相关理论教材与案例进行深入理解和复现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丨秋水丨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值