mirror of
https://github.com/intel/compute-runtime.git
synced 2026-01-25 22:34:15 +08:00
fix: use condition variables instead of busy waits in worker threads
Resolves: NEO-16085, GSD-11678, HSD-14025819208 Signed-off-by: Igor Venevtsev <igor.venevtsev@intel.com>
This commit is contained in:
committed by
Compute-Runtime-Automation
parent
6bee05e894
commit
9d1da44e08
@@ -16,7 +16,7 @@
|
||||
|
||||
namespace NEO {
|
||||
|
||||
TEST(DirectSubmissionControllerTestsMt, givenDirectSubmissionControllerWhenTimeoutThenDirectSubmissionsAreChecked) {
|
||||
TEST(DirectSubmissionControllerTestsMt, givenDirectSubmissionControllerWhenNewSubmissionThenDirectSubmissionsAreChecked) {
|
||||
MockExecutionEnvironment executionEnvironment;
|
||||
executionEnvironment.prepareRootDeviceEnvironments(1);
|
||||
executionEnvironment.initializeMemoryManager();
|
||||
@@ -28,29 +28,56 @@ TEST(DirectSubmissionControllerTestsMt, givenDirectSubmissionControllerWhenTimeo
|
||||
EngineDescriptorHelper::getDefaultDescriptor({aub_stream::ENGINE_CCS, EngineUsage::regular},
|
||||
PreemptionMode::ThreadGroup, deviceBitfield)));
|
||||
csr.setupContext(*osContext.get());
|
||||
csr.initializeTagAllocation();
|
||||
*csr.tagAddress = 9u;
|
||||
csr.taskCount.store(9u);
|
||||
|
||||
DirectSubmissionControllerMock controller;
|
||||
executionEnvironment.directSubmissionController.reset(&controller);
|
||||
controller.timeoutElapsedReturnValue.store(TimeoutElapsedMode::fullyElapsed);
|
||||
controller.startThread();
|
||||
csr.startControllingDirectSubmissions();
|
||||
controller.registerDirectSubmission(&csr);
|
||||
controller.startThread();
|
||||
// Controlling not started, wait until controller thread is waiting on condition var, no work done
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
while (controller.directSubmissions[&csr].taskCount != 9u) {
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
EXPECT_FALSE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_FALSE(controller.sleepCalled.load());
|
||||
EXPECT_FALSE(controller.checkNewSubmissionCalled.load());
|
||||
|
||||
// Start controlling, no submissions yet, wait until controller thread is waiting on condition var again
|
||||
controller.waitOnConditionVar.store(false);
|
||||
EXPECT_FALSE(controller.waitOnConditionVar.load());
|
||||
csr.startControllingDirectSubmissions();
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
while (!controller.directSubmissions[&csr].isStopped) {
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
EXPECT_TRUE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_TRUE(controller.sleepCalled.load());
|
||||
EXPECT_FALSE(controller.checkNewSubmissionCalled.load());
|
||||
|
||||
// Wake up controller with new submissions, work should be done and wait again
|
||||
controller.waitOnConditionVar.store(false);
|
||||
controller.handlePagingFenceRequestsCalled.store(false);
|
||||
controller.sleepCalled.store(false);
|
||||
EXPECT_FALSE(controller.waitOnConditionVar.load());
|
||||
EXPECT_FALSE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_FALSE(controller.sleepCalled.load());
|
||||
csr.taskCount = 10;
|
||||
controller.notifyNewSubmission();
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(controller.directSubmissionsMutex);
|
||||
EXPECT_NE(controller.directSubmissionControllingThread.get(), nullptr);
|
||||
EXPECT_TRUE(controller.directSubmissions[&csr].isStopped);
|
||||
EXPECT_EQ(controller.directSubmissions[&csr].taskCount, 9u);
|
||||
}
|
||||
// Work is done, verify results
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
EXPECT_TRUE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_TRUE(controller.sleepCalled.load());
|
||||
EXPECT_TRUE(controller.checkNewSubmissionCalled.load());
|
||||
EXPECT_NE(controller.directSubmissionControllingThread.get(), nullptr);
|
||||
EXPECT_TRUE(controller.directSubmissions[&csr].isStopped.load());
|
||||
EXPECT_EQ(10u, controller.directSubmissions[&csr].taskCount.load());
|
||||
EXPECT_EQ(10u, csr.peekTaskCount());
|
||||
|
||||
controller.stopThread();
|
||||
controller.unregisterDirectSubmission(&csr);
|
||||
executionEnvironment.directSubmissionController.release();
|
||||
@@ -62,9 +89,11 @@ TEST(DirectSubmissionControllerTestsMt, givenDirectSubmissionControllerWithStart
|
||||
EXPECT_NE(controller.directSubmissionControllingThread.get(), nullptr);
|
||||
controller.startControlling();
|
||||
|
||||
while (!controller.sleepCalled) {
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
controller.stopThread();
|
||||
}
|
||||
|
||||
@@ -73,9 +102,11 @@ TEST(DirectSubmissionControllerTestsMt, givenDirectSubmissionControllerWithNotSt
|
||||
controller.startThread();
|
||||
EXPECT_NE(controller.directSubmissionControllingThread.get(), nullptr);
|
||||
|
||||
while (!controller.sleepCalled) {
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
controller.stopThread();
|
||||
}
|
||||
|
||||
@@ -91,29 +122,67 @@ TEST(DirectSubmissionControllerTestsMt, givenDirectSubmissionControllerWhenEnque
|
||||
DirectSubmissionControllerMock controller;
|
||||
controller.sleepCalled.store(false);
|
||||
controller.startThread();
|
||||
while (!controller.sleepCalled) {
|
||||
|
||||
// No fence requests, wait until controller thread is waiting on condition var
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
EXPECT_FALSE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_EQ(0u, csr.pagingFenceValueToUnblock);
|
||||
|
||||
// Wake up controller with paging fence, work should be done and wait again
|
||||
controller.waitOnConditionVar = false;
|
||||
EXPECT_FALSE(controller.waitOnConditionVar.load());
|
||||
|
||||
controller.enqueueWaitForPagingFence(&csr, 10u);
|
||||
// Wait until csr is not updated
|
||||
while (csr.pagingFenceValueToUnblock == 0u) {
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
EXPECT_TRUE(controller.sleepCalled.load());
|
||||
EXPECT_TRUE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_EQ(10u, csr.pagingFenceValueToUnblock);
|
||||
|
||||
// Verify that controller is able to handle requests during controlling
|
||||
// Reset test state
|
||||
controller.waitOnConditionVar.store(false);
|
||||
controller.handlePagingFenceRequestsCalled.store(false);
|
||||
controller.sleepCalled.store(false);
|
||||
EXPECT_FALSE(controller.waitOnConditionVar.load());
|
||||
EXPECT_FALSE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_FALSE(controller.sleepCalled.load());
|
||||
|
||||
// Start controlling, no submissions yet, wait until controller thread is waiting on condition var again
|
||||
controller.startControlling();
|
||||
|
||||
controller.enqueueWaitForPagingFence(&csr, 20u);
|
||||
|
||||
while (csr.pagingFenceValueToUnblock == 10u) {
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
EXPECT_TRUE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_TRUE(controller.sleepCalled.load());
|
||||
EXPECT_FALSE(controller.checkNewSubmissionCalled.load());
|
||||
|
||||
// Reset test state again
|
||||
controller.waitOnConditionVar.store(false);
|
||||
controller.handlePagingFenceRequestsCalled.store(false);
|
||||
controller.sleepCalled.store(false);
|
||||
EXPECT_FALSE(controller.waitOnConditionVar.load());
|
||||
EXPECT_FALSE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_FALSE(controller.sleepCalled.load());
|
||||
|
||||
// Wake with peging fence in controlling state
|
||||
controller.enqueueWaitForPagingFence(&csr, 20u);
|
||||
while (!controller.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
EXPECT_TRUE(controller.waitOnConditionVar.load());
|
||||
EXPECT_TRUE(controller.handlePagingFenceRequestsCalled.load());
|
||||
EXPECT_TRUE(controller.sleepCalled.load());
|
||||
EXPECT_TRUE(controller.checkNewSubmissionCalled.load());
|
||||
EXPECT_EQ(20u, csr.pagingFenceValueToUnblock);
|
||||
|
||||
controller.stopThread();
|
||||
}
|
||||
|
||||
} // namespace NEO
|
||||
} // namespace NEO
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "shared/test/common/mocks/mock_memory_manager.h"
|
||||
#include "shared/test/common/mocks/mock_usm_memory_reuse_cleaner.h"
|
||||
#include "shared/test/common/test_macros/test.h"
|
||||
namespace NEO {
|
||||
|
||||
TEST(UnifiedMemoryReuseCleanerTestsMt, givenUnifiedMemoryReuseCleanerWhenSleepExpiredThenTrimOldInCachesIsCalled) {
|
||||
MockUnifiedMemoryReuseCleaner cleaner(false);
|
||||
TEST(UnifiedMemoryReuseCleanerTestsMt, givenUnifiedMemoryReuseCleanerWhenCachesAreEmptyThenWorkerThreadIsWaitingOnConditionVar) {
|
||||
MockMemoryManager mockMemoryManager;
|
||||
mockMemoryManager.executionEnvironment.unifiedMemoryReuseCleaner.reset(new MockUnifiedMemoryReuseCleaner(false));
|
||||
MockUnifiedMemoryReuseCleaner &cleaner = *static_cast<MockUnifiedMemoryReuseCleaner *>(mockMemoryManager.executionEnvironment.unifiedMemoryReuseCleaner.get());
|
||||
|
||||
cleaner.callBaseStartThread = true;
|
||||
cleaner.callBaseTrimOldInCaches = false;
|
||||
EXPECT_EQ(nullptr, cleaner.unifiedMemoryReuseCleanerThread);
|
||||
@@ -20,12 +24,34 @@ TEST(UnifiedMemoryReuseCleanerTestsMt, givenUnifiedMemoryReuseCleanerWhenSleepEx
|
||||
EXPECT_TRUE(cleaner.keepCleaning.load());
|
||||
|
||||
EXPECT_FALSE(cleaner.trimOldInCachesCalled);
|
||||
cleaner.registerSvmAllocationCache(nullptr);
|
||||
auto svmAllocCache = std::make_unique<SVMAllocsManager::SvmAllocationCache>();
|
||||
|
||||
constexpr size_t svmAllocSize = 1024;
|
||||
mockMemoryManager.usmReuseInfo.init(svmAllocSize, svmAllocSize);
|
||||
svmAllocCache->memoryManager = &mockMemoryManager;
|
||||
cleaner.registerSvmAllocationCache(svmAllocCache.get());
|
||||
EXPECT_TRUE(cleaner.runCleaning.load());
|
||||
|
||||
while (false == cleaner.trimOldInCachesCalled) {
|
||||
// Caches are empty, ensure cleaner thread is waiting on condition var
|
||||
while (!cleaner.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
EXPECT_TRUE(cleaner.waitOnConditionVar.load());
|
||||
EXPECT_TRUE(cleaner.isEmpty());
|
||||
EXPECT_FALSE(cleaner.trimOldInCachesCalled);
|
||||
|
||||
// Wake cleaner thread to proceed some data
|
||||
cleaner.waitOnConditionVar.store(false);
|
||||
EXPECT_FALSE(cleaner.waitOnConditionVar.load());
|
||||
SvmAllocationData allocData{0};
|
||||
svmAllocCache->insert(svmAllocSize, nullptr, &allocData, false);
|
||||
while (!cleaner.waitOnConditionVar.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
EXPECT_TRUE(cleaner.waitOnConditionVar.load());
|
||||
EXPECT_TRUE(cleaner.isEmpty());
|
||||
EXPECT_TRUE(cleaner.trimOldInCachesCalled);
|
||||
|
||||
cleaner.stopThread();
|
||||
EXPECT_EQ(nullptr, cleaner.unifiedMemoryReuseCleanerThread);
|
||||
EXPECT_FALSE(cleaner.runCleaning.load());
|
||||
@@ -43,4 +69,4 @@ TEST(UnifiedMemoryReuseCleanerTestsMt, givenUnifiedMemoryReuseCleanerWithNotStar
|
||||
cleaner.stopThread();
|
||||
}
|
||||
|
||||
} // namespace NEO
|
||||
} // namespace NEO
|
||||
|
||||
Reference in New Issue
Block a user