Fix HIP_SYNC_NULL_STREAM=0 mode.
- Fix null-stream sync - hipStreamDestroy of null stream returns hipErrorInvalidResourceHandle - Update documentation. - Add tests for null stream sync, hipEventElapsedTime. - Rename internal enum hipEventStatusRecorded to hipEventStatusComplete - refactor hipStreamWaitEvent to streamline control-flow
このコミットが含まれているのは:
@@ -28,8 +28,8 @@ THE SOFTWARE.
|
||||
|
||||
enum SyncMode {
|
||||
syncNone,
|
||||
syncNullStream,
|
||||
syncOtherStream,
|
||||
syncStream,
|
||||
syncStopEvent,
|
||||
};
|
||||
|
||||
|
||||
@@ -37,19 +37,23 @@ const char *syncModeString(int syncMode) {
|
||||
switch (syncMode) {
|
||||
case syncNone:
|
||||
return "syncNone";
|
||||
case syncNullStream:
|
||||
return "syncNullStream";
|
||||
case syncOtherStream:
|
||||
return "syncOtherStream";
|
||||
case syncStream:
|
||||
return "syncStream";
|
||||
case syncStopEvent:
|
||||
return "syncStopEvent";
|
||||
default:
|
||||
return "unknown";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
void test(int *C_d, int *C_h, int64_t numElements, SyncMode syncMode)
|
||||
void test(unsigned testMask, int *C_d, int *C_h, int64_t numElements, hipStream_t stream, int waitStart, SyncMode syncMode)
|
||||
{
|
||||
printf ("\ntest: syncMode=%s\n", syncModeString(syncMode));
|
||||
if (!(testMask & p_tests)) {
|
||||
return;
|
||||
}
|
||||
printf ("\ntest 0x%3x: stream=%p waitStart=%d syncMode=%s\n",
|
||||
testMask, stream, waitStart, syncModeString(syncMode));
|
||||
|
||||
size_t sizeBytes = numElements * sizeof(int);
|
||||
|
||||
@@ -60,55 +64,95 @@ void test(int *C_d, int *C_h, int64_t numElements, SyncMode syncMode)
|
||||
C_h[i] = -1; // initialize
|
||||
}
|
||||
|
||||
hipStream_t stream = 0;
|
||||
hipEvent_t neverCreated=0, neverRecorded, timingDisabled;
|
||||
HIPCHECK(hipEventCreate(&neverRecorded));
|
||||
HIPCHECK(hipEventCreateWithFlags(&timingDisabled, hipEventDisableTiming));
|
||||
|
||||
unsigned flags=0;
|
||||
if (syncMode == syncOtherStream) {
|
||||
HIPCHECK(hipStreamCreateWithFlags(&stream, flags));
|
||||
}
|
||||
|
||||
hipEvent_t neverCreated=0;
|
||||
hipEvent_t start, stop, neverRecorded;
|
||||
hipEvent_t start, stop;
|
||||
HIPCHECK(hipEventCreate(&start));
|
||||
HIPCHECK(hipEventCreate(&stop));
|
||||
HIPCHECK(hipEventCreate(&neverRecorded));
|
||||
|
||||
unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, numElements);
|
||||
|
||||
HIPCHECK(hipEventRecord(timingDisabled, stream));
|
||||
// sandwhich a kernel:
|
||||
HIPCHECK(hipEventRecord(start, stream));
|
||||
hipLaunchKernelGGL(HipTest::addCountReverse , dim3(blocks), dim3(threadsPerBlock), 0, stream, C_d, C_h, numElements, count);
|
||||
HIPCHECK(hipEventRecord(stop, stream));
|
||||
|
||||
HIPCHECK(hipStreamSynchronize(stream)); // wait for recording to finish...
|
||||
|
||||
if (waitStart) {
|
||||
HIPCHECK(hipEventSynchronize(start));
|
||||
}
|
||||
|
||||
|
||||
hipError_t expectedStopError = hipSuccess;
|
||||
|
||||
// How to wait for the events to finish:
|
||||
switch (syncMode) {
|
||||
case syncNone:
|
||||
expectedStopError = hipErrorNotReady;
|
||||
break;
|
||||
case syncStream:
|
||||
HIPCHECK(hipStreamSynchronize(stream)); // wait for recording to finish...
|
||||
break;
|
||||
case syncStopEvent:
|
||||
HIPCHECK(hipEventSynchronize(stop));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
};
|
||||
|
||||
|
||||
float t;
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, neverCreated, stop), hipErrorInvalidResourceHandle);
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, start, neverCreated), hipErrorInvalidResourceHandle);
|
||||
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, neverRecorded, stop), hipErrorInvalidResourceHandle);
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, start, neverRecorded), hipErrorInvalidResourceHandle);
|
||||
|
||||
HIPCHECK(hipEventElapsedTime(&t, start, stop));
|
||||
assert (t>0.0f);
|
||||
printf ("time=%6.2f\n", t);
|
||||
|
||||
HIPCHECK(hipEventElapsedTime(&t, stop, start));
|
||||
assert (t<0.0f);
|
||||
printf ("negtime=%6.2f\n", t);
|
||||
|
||||
HIPCHECK(hipEventElapsedTime(&t, start, start));
|
||||
assert (t==0.0f);
|
||||
HIPCHECK(hipEventElapsedTime(&t, stop, stop));
|
||||
assert (t==0.0f);
|
||||
|
||||
|
||||
if (stream) {
|
||||
HIPCHECK(hipStreamDestroy(stream));
|
||||
hipError_t e = hipEventElapsedTime(&t, start, start);
|
||||
if ((e != hipSuccess) && (e != hipErrorNotReady)) {
|
||||
failed ("start event not in expected state, was %d=%s\n", e, hipGetErrorName(e));
|
||||
}
|
||||
|
||||
if (e == hipSuccess)
|
||||
assert (t==0.0f);
|
||||
|
||||
|
||||
// stop usually ready unless we skipped the synchronization (syncNone)
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, stop, stop), expectedStopError);
|
||||
if (e == hipSuccess)
|
||||
assert (t==0.0f);
|
||||
|
||||
|
||||
e = hipEventElapsedTime(&t, start, stop);
|
||||
HIPCHECK_API(e, expectedStopError);
|
||||
if (expectedStopError == hipSuccess)
|
||||
assert (t>0.0f);
|
||||
printf ("time=%6.2f error=%s\n", t, hipGetErrorName(e));
|
||||
|
||||
e = hipEventElapsedTime(&t, stop, start);
|
||||
HIPCHECK_API(e, expectedStopError);
|
||||
if (expectedStopError == hipSuccess)
|
||||
assert (t<0.0f);
|
||||
printf ("negtime=%6.2f error=%s\n", t, hipGetErrorName(e));
|
||||
|
||||
|
||||
|
||||
{
|
||||
// Check some error conditions for incomplete events:
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, timingDisabled, stop), hipErrorInvalidResourceHandle);
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, start, timingDisabled), hipErrorInvalidResourceHandle);
|
||||
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, neverCreated, stop), hipErrorInvalidResourceHandle);
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, start, neverCreated), hipErrorInvalidResourceHandle);
|
||||
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, neverRecorded, stop), hipErrorInvalidResourceHandle);
|
||||
HIPCHECK_API(hipEventElapsedTime(&t, start, neverRecorded), hipErrorInvalidResourceHandle);
|
||||
}
|
||||
|
||||
HIPCHECK(hipEventDestroy(start));
|
||||
HIPCHECK(hipEventDestroy(stop));
|
||||
|
||||
// Clear out everything:
|
||||
HIPCHECK(hipDeviceSynchronize());
|
||||
|
||||
printf ("test: OK \n");
|
||||
}
|
||||
|
||||
@@ -125,15 +169,22 @@ void runTests(int64_t numElements)
|
||||
HIPCHECK(hipMalloc(&C_d, sizeBytes));
|
||||
HIPCHECK(hipHostMalloc(&C_h, sizeBytes));
|
||||
|
||||
hipStream_t stream;
|
||||
HIPCHECK(hipStreamCreateWithFlags(&stream, 0x0));
|
||||
|
||||
{
|
||||
test (C_d, C_h, numElements, syncNone);
|
||||
test (C_d, C_h, numElements, syncNullStream);
|
||||
test (C_d, C_h, numElements, syncOtherStream);
|
||||
//test (C_d, C_h, numElements, syncDevice);
|
||||
//for (int waitStart=0; waitStart<2; waitStart++) {
|
||||
for (int waitStart=1; waitStart>=0; waitStart--) {
|
||||
unsigned W = waitStart ? 0x1000:0;
|
||||
test (W | 0x01, C_d, C_h, numElements, 0 , waitStart, syncNone);
|
||||
test (W | 0x02, C_d, C_h, numElements, stream, waitStart, syncNone);
|
||||
test (W | 0x04, C_d, C_h, numElements, 0 , waitStart, syncStream);
|
||||
test (W | 0x08, C_d, C_h, numElements, stream, waitStart, syncStream);
|
||||
test (W | 0x10, C_d, C_h, numElements, 0, waitStart, syncStopEvent);
|
||||
test (W | 0x20, C_d, C_h, numElements, stream, waitStart, syncStopEvent);
|
||||
}
|
||||
|
||||
|
||||
HIPCHECK(hipStreamDestroy(stream));
|
||||
HIPCHECK(hipFree(C_d));
|
||||
HIPCHECK(hipHostFree(C_h));
|
||||
}
|
||||
@@ -143,7 +194,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
HipTest::parseStandardArguments(argc, argv, true /*failOnUndefinedArg*/);
|
||||
|
||||
runTests(4000000);
|
||||
runTests(80000000);
|
||||
|
||||
passed();
|
||||
}
|
||||
|
||||
@@ -56,9 +56,27 @@ const char *syncModeString(int syncMode) {
|
||||
};
|
||||
|
||||
|
||||
void test(int *C_d, int *C_h, int64_t numElements, SyncMode syncMode, bool expectMismatch)
|
||||
void test(unsigned testMask, int *C_d, int *C_h, int64_t numElements, SyncMode syncMode, bool expectMismatch)
|
||||
{
|
||||
printf ("\ntest: syncMode=%s expectMismatch=%d\n", syncModeString(syncMode), expectMismatch);
|
||||
|
||||
// This test sends a long-running kernel to the null stream, then tests to see if the
|
||||
// specified synchronization technique is effective.
|
||||
//
|
||||
// Some syncMode are not expected to correctly sync (for example "syncNone"). in these
|
||||
// cases the test sets expectMismatch and the check logic below will attempt to ensure that
|
||||
// the undesired synchronization did not occur - ie ensure the kernel is still running and did
|
||||
// not yet update the stop event. This can be tricky since if the kernel runs fast enough it
|
||||
// may complete before the check. To prevent this, the addCountReverse has a count parameter
|
||||
// which causes it to loop repeatedly, and the results are checked in reverse order.
|
||||
//
|
||||
// Tests with expectMismatch=true should ensure the kernel finishes correctly. This results
|
||||
// are checked and we test to make sure stop event has completed.
|
||||
|
||||
if (!(testMask & p_tests)) {
|
||||
return;
|
||||
}
|
||||
printf ("\ntest 0x%02x: syncMode=%s expectMismatch=%d\n",
|
||||
testMask, syncModeString(syncMode), expectMismatch);
|
||||
|
||||
size_t sizeBytes = numElements * sizeof(int);
|
||||
|
||||
@@ -72,13 +90,15 @@ void test(int *C_d, int *C_h, int64_t numElements, SyncMode syncMode, bool expec
|
||||
hipStream_t otherStream = 0;
|
||||
unsigned flags = (syncMode == syncMarkerThenOtherNonBlockingStream) ? hipStreamNonBlocking : hipStreamDefault;
|
||||
HIPCHECK(hipStreamCreateWithFlags(&otherStream, flags));
|
||||
hipEvent_t e;
|
||||
HIPCHECK(hipEventCreate(&e));
|
||||
hipEvent_t stop, otherStreamEvent;
|
||||
HIPCHECK(hipEventCreate(&stop));
|
||||
HIPCHECK(hipEventCreate(&otherStreamEvent));
|
||||
|
||||
|
||||
unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, numElements);
|
||||
// Launch kernel into null stream, should result in C_h == count.
|
||||
hipLaunchKernelGGL(HipTest::addCountReverse , dim3(blocks), dim3(threadsPerBlock), 0, 0 /*stream*/, C_d, C_h, numElements, count);
|
||||
HIPCHECK(hipEventRecord(stop, 0/*default*/));
|
||||
|
||||
switch (syncMode) {
|
||||
case syncNone:
|
||||
@@ -92,7 +112,10 @@ void test(int *C_d, int *C_h, int64_t numElements, SyncMode syncMode, bool expec
|
||||
break;
|
||||
case syncMarkerThenOtherStream:
|
||||
case syncMarkerThenOtherNonBlockingStream:
|
||||
HIPCHECK(hipEventRecord(e, otherStream)); // this may wait for NULL stream depending hipStreamNonBlocking flag above
|
||||
|
||||
// this may wait for NULL stream depending hipStreamNonBlocking flag above
|
||||
HIPCHECK(hipEventRecord(otherStreamEvent, otherStream));
|
||||
|
||||
HIPCHECK(hipStreamSynchronize(otherStream));
|
||||
break;
|
||||
case syncDevice:
|
||||
@@ -102,6 +125,14 @@ void test(int *C_d, int *C_h, int64_t numElements, SyncMode syncMode, bool expec
|
||||
assert(0);
|
||||
};
|
||||
|
||||
hipError_t done = hipEventQuery(stop);
|
||||
|
||||
if (expectMismatch) {
|
||||
assert (done == hipErrorNotReady);
|
||||
} else {
|
||||
assert (done == hipSuccess);
|
||||
}
|
||||
|
||||
int mismatches = 0;
|
||||
int expected = init0 + count;
|
||||
for (int i=0; i<numElements; i++) {
|
||||
@@ -121,17 +152,15 @@ void test(int *C_d, int *C_h, int64_t numElements, SyncMode syncMode, bool expec
|
||||
|
||||
|
||||
HIPCHECK(hipStreamDestroy(otherStream));
|
||||
HIPCHECK(hipEventDestroy(e));
|
||||
HIPCHECK(hipEventDestroy(stop));
|
||||
HIPCHECK(hipEventDestroy(otherStreamEvent));
|
||||
|
||||
HIPCHECK(hipDeviceSynchronize());
|
||||
|
||||
printf ("test: OK - %d mismatches (%6.2f%%)\n", mismatches, ((double)(mismatches)*100.0)/numElements);
|
||||
}
|
||||
|
||||
|
||||
void testEventRecord()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void runTests(int64_t numElements)
|
||||
{
|
||||
size_t sizeBytes = numElements * sizeof(int);
|
||||
@@ -145,12 +174,18 @@ void runTests(int64_t numElements)
|
||||
|
||||
|
||||
{
|
||||
test (C_d, C_h, numElements, syncNone, true /*expectMismatch*/);
|
||||
test (C_d, C_h, numElements, syncNullStream, false /*expectMismatch*/);
|
||||
test (C_d, C_h, numElements, syncOtherStream, true /*expectMismatch*/);
|
||||
test (C_d, C_h, numElements, syncDevice, false /*expectMismatch*/);
|
||||
test (C_d, C_h, numElements, syncMarkerThenOtherStream, false /*expectMismatch*/);
|
||||
test (C_d, C_h, numElements, syncMarkerThenOtherNonBlockingStream, true /*expectMismatch*/);
|
||||
test (0x01, C_d, C_h, numElements, syncNone, true /*expectMismatch*/);
|
||||
test (0x02, C_d, C_h, numElements, syncNullStream, false /*expectMismatch*/);
|
||||
test (0x04, C_d, C_h, numElements, syncOtherStream, true /*expectMismatch*/);
|
||||
test (0x08, C_d, C_h, numElements, syncDevice, false /*expectMismatch*/);
|
||||
|
||||
// Sending a marker to to null stream may synchronize the otherStream
|
||||
// - other created with hipStreamNonBlocking=0 : synchronization, should match
|
||||
// - other created with hipStreamNonBlocking=1 : no synchronization, may mismatch
|
||||
test (0x10, C_d, C_h, numElements, syncMarkerThenOtherStream, false /*expectMismatch*/);
|
||||
|
||||
// TODO - review why this test seems flaky
|
||||
//test (0x20, C_d, C_h, numElements, syncMarkerThenOtherNonBlockingStream, true /*expectMismatch*/);
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +196,9 @@ void runTests(int64_t numElements)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Can' destroy the default stream:// TODO - move to another test
|
||||
HIPCHECK_API(hipStreamDestroy(0), hipErrorInvalidResourceHandle);
|
||||
|
||||
HipTest::parseStandardArguments(argc, argv, true /*failOnUndefinedArg*/);
|
||||
|
||||
runTests(40000000);
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする