Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1648 lines
48 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. HashTest.cpp
  5. Abstract:
  6. Test harness for LKRhash
  7. Author:
  8. George V. Reilly (GeorgeRe) 06-Jan-1998
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Internet Information Server RunTime Library
  13. Revision History:
  14. --*/
  15. #include "precomp.hxx"
  16. DECLARE_DEBUG_PRINTS_OBJECT();
  17. #define HASHTEST_STATIC_DATA
  18. #include "WordHash.h"
  19. #include "IniFile.h"
  20. void test_iterators(double highload, int initsize, int nsubtbls,
  21. int nInsertIfNotFound);
  22. #ifdef LKR_STL_ITERATORS
  23. #pragma message("test STL iterators")
  24. void test_stl_iterators(double highload, int initsize, int nsubtbls);
  25. #endif // LKR_STL_ITERATORS
  26. #ifndef LKRHASH_KERNEL_MODE
  27. void print_table_statistics(const CLKRHashTableStats& stats);
  28. # ifdef LOCK_INSTRUMENTATION
  29. void print_lock_statistics(const CLKRHashTableStats &stats);
  30. # endif
  31. #endif // !LKRHASH_KERNEL_MODE
  32. int expand_key_set(int maxkeys, int numkeys, bool fVerbose) ;
  33. #ifdef LKRHASH_KERNEL_MODE
  34. void
  35. #else
  36. unsigned __stdcall
  37. #endif
  38. exercise_table(void *pinput);
  39. // how many CPUs on this machine?
  40. int
  41. NumProcessors()
  42. {
  43. static int s_nCPUs = 0;
  44. if (s_nCPUs == 0)
  45. {
  46. #ifdef LKRHASH_KERNEL_MODE
  47. s_nCPUs = KeNumberProcessors;
  48. #else // !LKRHASH_KERNEL_MODE
  49. SYSTEM_INFO si;
  50. GetSystemInfo(&si);
  51. s_nCPUs = si.dwNumberOfProcessors;
  52. #endif // !LKRHASH_KERNEL_MODE
  53. }
  54. return s_nCPUs;
  55. }
  56. // globals
  57. int g_nokeys=0 ;
  58. CWord g_wordtable[MAXKEYS];
  59. bool
  60. CheckRefCounts(
  61. LONG nRef,
  62. int iFirst = 0,
  63. int iLast = -1)
  64. {
  65. if (iLast == -1)
  66. iLast = g_nokeys;
  67. IRTLTRACE3("\nCheckRefCounts(%d, [%d,%d))\n", nRef, iFirst, iLast);
  68. bool f = true;
  69. for (int i = iFirst; i != iLast; ++i)
  70. {
  71. f = f && (g_wordtable[i].m_cRefs == nRef);
  72. if (g_wordtable[i].m_cRefs != nRef)
  73. IRTLTRACE4("\nCRC: %d, %hs, expected %d, got %d\n",
  74. i, g_wordtable[i].m_str.m_psz, nRef,
  75. g_wordtable[i].m_cRefs);
  76. IRTLASSERT(g_wordtable[i].m_cRefs == nRef);
  77. }
  78. return f;
  79. }
  80. bool CWordHash::sm_fCaseInsensitive = true;
  81. bool CWordHash::sm_fMemCmp = false;
  82. int CWordHash::sm_nLastChars = 16;
  83. bool CWordHash::sm_fRefTrace = false;
  84. bool CWordHash::sm_fNonPagedAllocs = true;
  85. struct thread_data
  86. {
  87. CWordHash* ptbl ;
  88. int threadno ;
  89. int first_key ;
  90. int last_key ;
  91. int rounds ;
  92. int lookup_freq ;
  93. float highload ;
  94. int cinserts ;
  95. int cdeletes ;
  96. int clookups ;
  97. int cfailures ;
  98. int m_nInsertIfNotFound;
  99. int m_nFindKeyCopy;
  100. int m_nSeed; // random seed
  101. double duration ;
  102. HANDLE hevFinished;
  103. HANDLE hThread;
  104. } ;
  105. const TCHAR*
  106. CommaNumber(
  107. int n,
  108. TCHAR* ptszBuff)
  109. {
  110. TCHAR* ptsz = ptszBuff;
  111. TCHAR chComma = '\0';
  112. int aThousands[4];
  113. int iThousands = 0;
  114. unsigned int u = n;
  115. if (n < 0)
  116. {
  117. *ptsz++ = '-';
  118. u = -n;
  119. }
  120. do {
  121. aThousands[iThousands++] = u % 1000;
  122. u /= 1000;
  123. } while (u != 0);
  124. while (--iThousands >= 0)
  125. {
  126. u = aThousands[iThousands];
  127. if (chComma)
  128. *ptsz++ = chComma;
  129. unsigned d = u % 10;
  130. u /= 10;
  131. unsigned t = u % 10;
  132. u /= 10;
  133. unsigned h = u;
  134. if (h > 0 || chComma)
  135. *ptsz++ = h + '0';
  136. if (t > 0 || h > 0 || chComma)
  137. *ptsz++ = t + '0';
  138. *ptsz++ = d + '0';
  139. chComma = ',';
  140. }
  141. *ptsz = '\0';
  142. return ptszBuff;
  143. }
  144. #ifndef LKRHASH_KERNEL_MODE
  145. typedef union {
  146. FILETIME ft;
  147. __int64 l64;
  148. } FILETIME_UINT64;
  149. # define FILETIME_1_SECOND 10000000
  150. # define FILETIME_1_MILLISECOND 10000
  151. HANDLE
  152. HashTestCreateEvent()
  153. {
  154. return CreateEvent(NULL, // no security attributes
  155. FALSE, // auto reset
  156. FALSE, // not signalled
  157. NULL); // no name
  158. }
  159. void
  160. HashTestResumeThread(
  161. HANDLE hThread)
  162. {
  163. ResumeThread(hThread);
  164. }
  165. HANDLE
  166. HashTestCreateThread(
  167. unsigned (__stdcall * pfnThreadProc) (void *),
  168. void* pvContext,
  169. bool fSuspended)
  170. {
  171. unsigned dummy;
  172. return (HANDLE) _beginthreadex(NULL, 0, pfnThreadProc, pvContext,
  173. fSuspended ? CREATE_SUSPENDED : 0,
  174. &dummy);
  175. }
  176. DWORD
  177. HashTestWaitForMultipleObjects(
  178. DWORD nCount,
  179. CONST HANDLE *lpHandles)
  180. {
  181. return WaitForMultipleObjects(nCount, lpHandles, TRUE, INFINITE);
  182. }
  183. #else // LKRHASH_KERNEL_MODE
  184. # define GetTickCount() NtGetTickCount()
  185. # define GetCurrentThread() NtCurrentThread()
  186. void
  187. SetThreadIdealProcessor(
  188. HANDLE hThread,
  189. DWORD dwIdealProcessor
  190. )
  191. {
  192. NtSetInformationThread(
  193. hThread,
  194. ThreadIdealProcessor,
  195. &dwIdealProcessor,
  196. sizeof(dwIdealProcessor)
  197. );
  198. }
  199. // non-threadsafe implementation of rand and srand, stolen from CRT
  200. unsigned long _holdrand = 1234567890;
  201. void __cdecl
  202. srand(
  203. unsigned int seed)
  204. {
  205. _holdrand = (unsigned long) seed;
  206. }
  207. int __cdecl
  208. rand()
  209. {
  210. return ((_holdrand = _holdrand * 214013L + 2531011L) >> 16) & 0x7fff;
  211. }
  212. HANDLE
  213. HashTestCreateEvent()
  214. {
  215. HANDLE hEvent = NULL;
  216. NTSTATUS status = NtCreateEvent(
  217. &hEvent,
  218. EVENT_ALL_ACCESS,
  219. NULL,
  220. SynchronizationEvent,
  221. FALSE);
  222. return hEvent;
  223. }
  224. void
  225. HashTestResumeThread(
  226. HANDLE hThread)
  227. {
  228. NtResumeThread(hThread, NULL);
  229. }
  230. HANDLE
  231. HashTestCreateThread(
  232. void (* pfnThreadProc) (void *),
  233. void* pvContext,
  234. bool fSuspended)
  235. {
  236. NTSTATUS status;
  237. HANDLE threadHandle;
  238. OBJECT_ATTRIBUTES objectAttributes;
  239. //
  240. // Create the thread.
  241. //
  242. InitializeObjectAttributes(
  243. &objectAttributes, // ObjectAttributes
  244. NULL, // ObjectName
  245. OBJ_KERNEL_HANDLE, // Attributes
  246. NULL, // RootDirectory
  247. NULL // SecurityDescriptor
  248. );
  249. status = PsCreateSystemThread(
  250. &threadHandle, // ThreadHandle
  251. THREAD_ALL_ACCESS, // DesiredAccess
  252. &objectAttributes, // ObjectAttributes
  253. NULL, // ProcessHandle
  254. NULL, // ClientId
  255. pfnThreadProc, // StartRoutine
  256. pvContext // StartContext
  257. );
  258. if (!fSuspended)
  259. HashTestResumeThread(threadHandle);
  260. return threadHandle;
  261. }
  262. BOOL
  263. CloseHandle(
  264. HANDLE h)
  265. {
  266. return NT_SUCCESS(NtClose(h));
  267. }
  268. DWORD
  269. HashTestWaitForMultipleObjects(
  270. DWORD nCount,
  271. CONST HANDLE *lpHandles)
  272. {
  273. HANDLE ahHandles[MAX_THREADS+1];
  274. for (int i = 0; i < nCount; ++i)
  275. ahHandles[i] = lpHandles[i];
  276. return NtWaitForMultipleObjects((CHAR) nCount, ahHandles,
  277. WaitAll, FALSE, NULL);
  278. }
  279. BOOL
  280. SetEvent(
  281. HANDLE hEvent)
  282. {
  283. return NT_SUCCESS(NtSetEvent(hEvent, NULL));
  284. }
  285. #endif // LKRHASH_KERNEL_MODE
  286. #ifdef _M_IX86
  287. // Use RDTSC to read timestamp
  288. void
  289. GetCycleCount(
  290. LARGE_INTEGER *pliTimeStamp)
  291. {
  292. ULONG Lo;
  293. LONG Hi;
  294. _asm {
  295. _emit 0x0f
  296. _emit 0x31
  297. mov Lo, eax
  298. mov Hi, edx
  299. } /* _asm */
  300. pliTimeStamp->LowPart = Lo;
  301. pliTimeStamp->HighPart = Hi;
  302. }
  303. #endif // _M_IX86
  304. int
  305. LKR_TestHashTable(
  306. CIniFileSettings& ifs)
  307. {
  308. CWordHash *pTbl ;
  309. int num_threads ;
  310. thread_data de_area[MAX_THREADS] ;
  311. HANDLE ahEvents[MAX_THREADS];
  312. TCHAR tsz[1024] ;
  313. FILE *fp ;
  314. int nodel=0;
  315. int keys_per_thread ;
  316. int i ;
  317. int sum_ins, sum_dels, sum_lookups ;
  318. int failures = 0, total_failures = 0;
  319. bool fVerbose = false;
  320. double dblSumDuration3 = 0;
  321. DWORD dwRunTime = GetTickCount();
  322. int nBaseOps = 0;
  323. #ifdef _NO_TRACING_
  324. CREATE_DEBUG_PRINT_OBJECT("hashtest");
  325. #endif
  326. SetThreadIdealProcessor(GetCurrentThread(), 0);
  327. _tprintf(_TEXT("\nTest driver for LKRhash\n")
  328. #ifdef LKRHASH_KERNEL_MODE
  329. _TEXT(" (Kernel)")
  330. #endif
  331. #ifdef IRTLDEBUG
  332. _TEXT(" (Debug)")
  333. #endif
  334. #ifdef LKR_PUBLIC_API
  335. _TEXT(" (Public API)")
  336. #else
  337. _TEXT(" (Internal API)")
  338. #endif
  339. #ifdef LKR_COUNTDOWN
  340. _TEXT(" (CountDown)")
  341. #else
  342. _TEXT(" (CountUp)")
  343. #endif
  344. #ifdef LKR_CONTRACT
  345. _TEXT(" (Contraction)")
  346. #else
  347. _TEXT(" (No Contraction)")
  348. #endif
  349. #ifdef LOCKS_SWITCH_TO_THREAD
  350. _TEXT(" (SwitchToThread)\n")
  351. #endif
  352. #ifdef LKR_DEPRECATED_ITERATORS
  353. _TEXT(" (Deprecated Iterators)")
  354. #endif
  355. #ifdef LKR_APPLY_IF
  356. _TEXT(" (ApplyIf)")
  357. #endif
  358. #ifdef LKR_EXPOSED_TABLE_LOCK
  359. _TEXT(" (Exposed Table Lock)")
  360. #endif
  361. #ifdef LKR_STL_ITERATORS
  362. _TEXT(" (STL-style Iterators")
  363. # if LKR_STL_ITERATORS >= 2
  364. _TEXT(", verbose)")
  365. # else
  366. _TEXT(")")
  367. # endif
  368. #else // !LKR_STL_ITERATORS
  369. _TEXT(" (No STL-style Iterators)")
  370. #endif // !LKR_STL_ITERATORS
  371. _TEXT("\n\n")
  372. ) ;
  373. #if defined(LKRHASH_ACACHE)
  374. const TCHAR tszAllocator[] = _TEXT("ACache");
  375. #elif defined(LKRHASH_ROCKALL_FAST)
  376. const TCHAR tszAllocator[] = _TEXT("Rockall FAST_HEAP");
  377. #elif defined(LKRHASH_PAGEDHEAP)
  378. const TCHAR tszAllocator[] = _TEXT("CPagedHeap");
  379. #elif defined(LKRHASH_NONPAGEDHEAP)
  380. const TCHAR tszAllocator[] = _TEXT("CNonPagedHeap");
  381. #elif defined(LKRHASH_NONPAGEDLOOKASIDE)
  382. const TCHAR tszAllocator[] = _TEXT("CNonPagedLookasideList");
  383. #elif defined(LKRHASH_PAGEDLOOKASIDE)
  384. const TCHAR tszAllocator[] = _TEXT("CPagedLookasideList");
  385. #else
  386. const TCHAR tszAllocator[] =
  387. _TEXT("Default allocator (global operator new)");
  388. #endif
  389. _tprintf(_TEXT("%s version.\n"), tszAllocator);
  390. #ifdef SAMPLE_LKRHASH_TESTCLASS
  391. Test(fVerbose);
  392. if (fVerbose)
  393. _tprintf(_TEXT("Test succeeded\n"));
  394. #endif // SAMPLE_LKRHASH_TESTCLASS
  395. fp = _tfopen(ifs.m_tszDataFile, _TEXT("r") ) ;
  396. if (fp == NULL)
  397. {
  398. _tprintf(_TEXT("Can't open file `%s'.\n"), ifs.m_tszDataFile) ;
  399. return 1;
  400. }
  401. char sz[1024];
  402. _tprintf(_TEXT("Reading `%s' "), ifs.m_tszDataFile);
  403. for (g_nokeys = 0; g_nokeys < ifs.m_nMaxKeys; )
  404. {
  405. if (fgets(sz, sizeof(sz)/sizeof(sz[0]), fp) == NULL)
  406. break;
  407. int cch = strlen(sz);
  408. // TODO: check for duplicates
  409. if (cch > 0 && sz[cch-1] == '\n')
  410. sz[--cch] = '\0';
  411. if (cch >= MAX_STRSIZE)
  412. sz[MAX_STRSIZE-1] = '\0';
  413. if (cch > 0)
  414. g_wordtable[g_nokeys++].m_str.Set(sz, cch);
  415. if (g_nokeys % 10000 == 0)
  416. putchar('.');
  417. }
  418. fclose(fp) ;
  419. _tprintf(_TEXT("\nLoaded %s keys from `%s', "),
  420. CommaNumber(g_nokeys, tsz), ifs.m_tszDataFile);
  421. g_nokeys = expand_key_set(ifs.m_nMaxKeys, g_nokeys, true) ;
  422. _tprintf(_TEXT(" expanded to %s keys.\n\n"),
  423. CommaNumber(g_nokeys, tsz));
  424. int cchTotal = 0, cchMin = INT_MAX, cchMax = 0;
  425. for (i = 0; i < g_nokeys; ++i)
  426. {
  427. cchTotal += g_wordtable[i].m_str.m_cch;
  428. cchMin = min(cchMin, g_wordtable[i].m_str.m_cch);
  429. cchMax = max(cchMax, g_wordtable[i].m_str.m_cch);
  430. }
  431. srand(ifs.m_nSeed) ;
  432. _stprintf(tsz, _TEXT("%d"), ifs.m_nInitSize);
  433. if (ifs.m_nInitSize == LK_SMALL_TABLESIZE)
  434. _tcscpy(tsz, _TEXT("small"));
  435. else if (ifs.m_nInitSize == LK_MEDIUM_TABLESIZE)
  436. _tcscpy(tsz, _TEXT("medium"));
  437. else if (ifs.m_nInitSize == LK_LARGE_TABLESIZE)
  438. _tcscpy(tsz, _TEXT("large"));
  439. DWORD initsize2 = ifs.m_nInitSize;
  440. DWORD nsubtbls2 = ifs.m_nSubTables;
  441. LK_TABLESIZE lkts = CWordHash::NumSubTables(ifs.m_nInitSize, nsubtbls2);
  442. _tprintf(_TEXT("Max load=%.1f, initsize=%s, ")
  443. _TEXT("%d subtables (%d tables, size=%d, lkts=%d).\n"),
  444. ifs.m_dblHighLoad, tsz,
  445. ifs.m_nSubTables, nsubtbls2, initsize2, lkts);
  446. _tprintf(_TEXT("Lookup freq = %d, %d-%d threads, ")
  447. _TEXT("%d round%s.\n"),
  448. ifs.m_nLookupFreq, ifs.m_nMinThreads, ifs.m_nMaxThreads,
  449. ifs.m_nRounds, (ifs.m_nRounds==1 ? "" : "s"));
  450. _tprintf(_TEXT("%s keys from `%s'.\n"),
  451. CommaNumber(g_nokeys, tsz), ifs.m_tszDataFile);
  452. _tprintf(_TEXT("Key length: avg = %d, min = %d, max = %d.\n"),
  453. cchTotal / g_nokeys, cchMin, cchMax);
  454. _tprintf(_TEXT("Base Table = %s. Hash method = %s.\n"),
  455. CWordHash::ClassName(), CWordHash::HashMethod());
  456. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  457. # ifdef LKRHASH_GLOBAL_LOCK
  458. _tprintf(_TEXT("GlobalLock = %s, ")
  459. _TEXT("%d bytes, ")
  460. _TEXT("Spin Count = %hd, ")
  461. _TEXT("Adj Factor = %.1f.\n"),
  462. CWordHash::GlobalLock::ClassName(),
  463. sizeof(CWordHash::GlobalLock),
  464. ifs.m_wTableSpin,
  465. CWordHash::GlobalLock::GetDefaultSpinAdjustmentFactor());
  466. # endif
  467. _tprintf(_TEXT("TableLock = %s, ")
  468. _TEXT("%d bytes, ")
  469. _TEXT("Spin Count = %hd, ")
  470. _TEXT("Adj Factor = %.1f.\n"),
  471. CWordHash::TableLock::ClassName(),
  472. sizeof(CWordHash::TableLock),
  473. ifs.m_wTableSpin,
  474. CWordHash::TableLock::GetDefaultSpinAdjustmentFactor());
  475. _tprintf(_TEXT("BucketLock = %s, ")
  476. _TEXT("%d bytes, ")
  477. _TEXT("Spin Count = %hd, ")
  478. _TEXT("Adj Factor = %.1f.\n"),
  479. CWordHash::BucketLock::ClassName(),
  480. sizeof(CWordHash::BucketLock),
  481. ifs.m_wBucketSpin,
  482. CWordHash::BucketLock::GetDefaultSpinAdjustmentFactor());
  483. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  484. #ifdef LOCK_PER_LOCK_SPINCOUNTS
  485. _tprintf(_TEXT("Per"));
  486. #else
  487. _tprintf(_TEXT("No per"));
  488. #endif
  489. _tprintf(_TEXT("-lock spincounts. #CPUs = %d. Random seed = %d. ")
  490. _TEXT("Nodes/Clump = %d.\n"),
  491. NumProcessors(), ifs.m_nSeed,
  492. CWordHash::NODES_PER_CLUMP
  493. );
  494. _tprintf(_TEXT("InsertIfNotFound = %d, FindKeyCopy = %d\n"),
  495. ifs.m_nInsertIfNotFound, ifs.m_nFindKeyCopy);
  496. _tprintf(_TEXT("NonPagedAllocs=%d, RefTrace=%d, Allocator=%s.\n"),
  497. ifs.m_fNonPagedAllocs, ifs.m_fRefTrace,
  498. CLKRhashAllocator::ClassName());
  499. #ifndef LKRHASH_KERNEL_MODE
  500. time_t tmNow;
  501. time(&tmNow);
  502. _tprintf(_TEXT("\nRun: %s\n\n"), _tctime(&tmNow));
  503. #endif // !LKRHASH_KERNEL_MODE
  504. if (ifs.m_fTestIterators)
  505. {
  506. test_iterators(ifs.m_dblHighLoad, ifs.m_nInitSize,
  507. ifs.m_nSubTables, ifs.m_nInsertIfNotFound);
  508. #ifdef LKR_STL_ITERATORS
  509. test_stl_iterators(ifs.m_dblHighLoad, ifs.m_nInitSize,
  510. ifs.m_nSubTables);
  511. #endif // LKR_STL_ITERATORS
  512. }
  513. #ifndef LKRHASH_KERNEL_MODE
  514. // set multimedia timer's period to be 1 millisecond (or the closest
  515. // approximation that the hardware can manage). This is usually more
  516. // accurate than GetTickCount. I have had very dubious results from
  517. // QueryPerformanceCounter on multiprocessor machines, including
  518. // negative(!) durations (timer skew between processors?)
  519. timeBeginPeriod(1);
  520. #endif // !LKRHASH_KERNEL_MODE
  521. _tprintf(_TEXT("Starting threads...\n\n"));
  522. int nTotalOps = 0;
  523. int step = (ifs.m_nMinThreads <= ifs.m_nMaxThreads) ? +1 : -1;
  524. dwRunTime = GetTickCount() - dwRunTime;
  525. for (num_threads = ifs.m_nMinThreads;
  526. num_threads != ifs.m_nMaxThreads + step;
  527. num_threads += step )
  528. {
  529. IRTLTRACE1("\nStarting %8d\n", num_threads);
  530. pTbl = new CWordHash(ifs.m_dblHighLoad, ifs.m_nInitSize,
  531. ifs.m_nSubTables) ;
  532. pTbl->SetTableLockSpinCount(ifs.m_wTableSpin);
  533. pTbl->SetBucketLockSpinCount(ifs.m_wBucketSpin);
  534. keys_per_thread = g_nokeys/num_threads ;
  535. for (i = 0; i < num_threads; i++)
  536. {
  537. de_area[i].ptbl = pTbl ;
  538. de_area[i].threadno = i+1 ;
  539. de_area[i].first_key = i*keys_per_thread ;
  540. de_area[i].last_key = ((i == num_threads - 1)
  541. ? g_nokeys
  542. : (i+1)*keys_per_thread) ;
  543. de_area[i].rounds = ifs.m_nRounds ;
  544. de_area[i].highload = (float) ifs.m_dblHighLoad ;
  545. de_area[i].lookup_freq = ifs.m_nLookupFreq ;
  546. de_area[i].m_nInsertIfNotFound = ifs.m_nInsertIfNotFound;
  547. de_area[i].m_nFindKeyCopy = ifs.m_nFindKeyCopy;
  548. de_area[i].m_nSeed = ifs.m_nSeed;
  549. de_area[i].hevFinished = HashTestCreateEvent();
  550. IRTLASSERT(de_area[i].hevFinished != NULL);
  551. ahEvents[i] = de_area[i].hevFinished;
  552. de_area[i].hThread = HashTestCreateThread(exercise_table,
  553. &de_area[i], true);
  554. }
  555. #ifndef LKRHASH_KERNEL_MODE
  556. DWORD dwMMT1 = timeGetTime();
  557. #endif // !LKRHASH_KERNEL_MODE
  558. for (i = 0; i < num_threads; i++)
  559. {
  560. HashTestResumeThread(de_area[i].hThread);
  561. CloseHandle(de_area[i].hThread);
  562. }
  563. DWORD dw = HashTestWaitForMultipleObjects(num_threads, ahEvents);
  564. #ifndef LKRHASH_KERNEL_MODE
  565. DWORD dwMMT2 = timeGetTime();
  566. #endif // !LKRHASH_KERNEL_MODE
  567. for (i = 0; i < num_threads; i++)
  568. CloseHandle(ahEvents[i]);
  569. #ifndef LKRHASH_KERNEL_MODE
  570. double duration3 = double(dwMMT2 - dwMMT1) / 1000.0;
  571. dblSumDuration3 += duration3;
  572. dwRunTime += dwMMT2 - dwMMT1;
  573. #endif // !LKRHASH_KERNEL_MODE
  574. sum_ins = sum_dels = sum_lookups = 0 ;
  575. for (i = 0; i < num_threads; i++)
  576. {
  577. sum_ins += de_area[i].cinserts ;
  578. sum_dels += de_area[i].cdeletes ;
  579. sum_lookups += de_area[i].clookups ;
  580. failures += de_area[i].cfailures ;
  581. }
  582. int nOps = sum_ins + sum_dels + sum_lookups;
  583. total_failures += failures;
  584. nTotalOps += nOps; // TODO: weight?
  585. #ifdef LKRHASH_KERNEL_MODE
  586. #else // !LKRHASH_KERNEL_MODE
  587. int nOpsRate3 = (int)(nOps / duration3);
  588. if (num_threads == ifs.m_nMinThreads)
  589. nBaseOps = nOpsRate3;
  590. TCHAR tszSumIns[16], tszSumDels[16], tszSumLookups[16];
  591. TCHAR tszNOps3[16];
  592. #ifndef LOCK_INSTRUMENTATION
  593. if (num_threads == ifs.m_nMinThreads)
  594. #endif // LOCK_INSTRUMENTATION
  595. {
  596. _tprintf(_TEXT("%5s %10s %9s %6s")
  597. _TEXT("%8s %8s %8s\n"),
  598. _TEXT("Thrds"), _TEXT("Ops/sec"),
  599. _TEXT("Duration"), _TEXT("Ratio"),
  600. _TEXT("Inserts"), _TEXT("Deletes"), _TEXT("Lookups"));
  601. }
  602. TCHAR tszSummary[200];
  603. _stprintf(tszSummary, _TEXT("%5d %10s %9.3f %6.3f")
  604. _TEXT("%7sK %7sK %7sK\n"),
  605. num_threads,
  606. CommaNumber(nOpsRate3, tszNOps3),
  607. duration3,
  608. double(nOpsRate3) / double(nBaseOps),
  609. CommaNumber((sum_ins + 500) / 1000, tszSumIns),
  610. CommaNumber((sum_dels + 500) / 1000, tszSumDels),
  611. CommaNumber((sum_lookups + 500) / 1000, tszSumLookups)
  612. );
  613. _tprintf(tszSummary);
  614. IRTLTRACE1("%s", tszSummary);
  615. if (failures != 0)
  616. _tprintf(_TEXT("%d failed operations!\n"), failures);
  617. #endif // !LKRHASH_KERNEL_MODE
  618. #ifdef LOCK_INSTRUMENTATION
  619. print_lock_statistics(pTbl->GetStatistics());
  620. #ifdef LKRHASH_GLOBAL_LOCK
  621. CWordHash::GlobalLock::ResetGlobalStatistics();
  622. #endif
  623. CWordHash::BucketLock::ResetGlobalStatistics();
  624. CWordHash::TableLock::ResetGlobalStatistics();
  625. _tprintf(_TEXT("\n"));
  626. #endif
  627. delete pTbl ;
  628. }
  629. TCHAR tszNTotalOps3[16];
  630. _tprintf(_TEXT("\nAverage Ops = %s. RunTime = %d:%02d.%03d.\n"),
  631. CommaNumber(int(nTotalOps / dblSumDuration3), tszNTotalOps3),
  632. dwRunTime / 60000, (dwRunTime / 1000) % 60, dwRunTime % 1000);
  633. if (total_failures != 0)
  634. _tprintf(_TEXT("%d total failed operations!\n"), total_failures);
  635. #ifndef LKRHASH_KERNEL_MODE
  636. timeEndPeriod(1);
  637. #endif // !LKRHASH_KERNEL_MODE
  638. return 0;
  639. } // LKR_TestHashTable
  640. void test_iterators(
  641. double highload,
  642. int initsize,
  643. int nsubtbls,
  644. int nInsertIfNotFound)
  645. {
  646. _tprintf(_TEXT("Testing iterators...\n"));
  647. int i;
  648. CWordHash *pTbl = new CWordHash(highload, initsize, nsubtbls) ;
  649. LK_RETCODE lkrc;
  650. IRTLASSERT(0 == pTbl->Size());
  651. IRTLASSERT(pTbl->CheckTable() == 0);
  652. IRTLTRACE0("Table is empty. Building...\n");
  653. int cInsertIfNotFounds = 0;
  654. for (i = 0 ; i < g_nokeys ; i++ )
  655. {
  656. lkrc = pTbl->InsertRecord(&g_wordtable[i], false);
  657. if (lkrc != LK_SUCCESS)
  658. IRTLTRACE3("i = %d, word = `%hs', lkrc = %d\n",
  659. i, g_wordtable[i].m_str.m_psz, lkrc);
  660. IRTLASSERT(lkrc == LK_SUCCESS);
  661. #ifdef LKR_EXPOSED_TABLE_LOCK
  662. if (nInsertIfNotFound > 0 && rand() % nInsertIfNotFound == 0)
  663. {
  664. pTbl->WriteLock();
  665. int x = rand() % g_nokeys;
  666. CStr* pstrKey = &g_wordtable[x].m_str;
  667. CWord* pRec = NULL;
  668. lkrc = pTbl->FindKey(pstrKey, &pRec);
  669. if (pRec != NULL)
  670. {
  671. IRTLASSERT(lkrc == LK_SUCCESS);
  672. IRTLASSERT(pRec == &g_wordtable[x]);
  673. IRTLASSERT(x <= i);
  674. --g_wordtable[x].m_cRefs;
  675. }
  676. else
  677. {
  678. ++cInsertIfNotFounds;
  679. IRTLASSERT(x > i);
  680. IRTLASSERT(lkrc == LK_NO_SUCH_KEY);
  681. lkrc = pTbl->InsertRecord(&g_wordtable[x], false);
  682. IRTLASSERT(lkrc == LK_SUCCESS);
  683. InterlockedIncrement(&g_wordtable[x].m_cInsertIfNotFounds);
  684. lkrc = pTbl->DeleteKey(&g_wordtable[x].m_str);
  685. IRTLASSERT(lkrc == LK_SUCCESS);
  686. }
  687. pTbl->WriteUnlock();
  688. }
  689. #endif // LKR_EXPOSED_TABLE_LOCK
  690. }
  691. IRTLTRACE1("cInsertIfNotFounds = %d\n", cInsertIfNotFounds);
  692. #ifdef LKR_EXPOSED_TABLE_LOCK
  693. pTbl->ReadLock();
  694. IRTLTRACE2("Checking that table has %d records (size = %d)\n",
  695. g_nokeys, pTbl->Size());
  696. IRTLASSERT(g_nokeys == (int) pTbl->Size());
  697. IRTLASSERT(pTbl->CheckTable() == 0);
  698. pTbl->ReadUnlock();
  699. #endif // LKR_EXPOSED_TABLE_LOCK
  700. IRTLTRACE0("Clearing the table\n");
  701. pTbl->Clear();
  702. IRTLASSERT(0 == pTbl->Size());
  703. IRTLASSERT(pTbl->CheckTable() == 0);
  704. IRTLTRACE0("Seeing what crud is left in the table\n");
  705. size_t cRec = 0;
  706. for (i = 0 ; i < g_nokeys ; i++ )
  707. {
  708. CStr* pstrKey = &g_wordtable[i].m_str;
  709. CWord* pRec = NULL;
  710. LK_RETCODE lkrc = pTbl->FindKey(pstrKey, &pRec);
  711. if (pRec != NULL)
  712. {
  713. IRTLASSERT(pRec == &g_wordtable[i]);
  714. --pRec->m_cRefs;
  715. IRTLTRACE1("%hs\n", g_wordtable[i].m_str.m_psz);
  716. ++cRec;
  717. }
  718. }
  719. IRTLTRACE1("Found %d records that shouldn't have been there\n", cRec);
  720. pTbl->Clear();
  721. delete pTbl;
  722. pTbl = new CWordHash(highload, initsize, nsubtbls) ;
  723. IRTLTRACE0("Rebuilding the table\n");
  724. for (i = 0 ; i < g_nokeys ; i++ )
  725. IRTLVERIFY(pTbl->InsertRecord(&g_wordtable[i]) == LK_SUCCESS);
  726. IRTLASSERT(g_nokeys == (int) pTbl->Size());
  727. IRTLASSERT(pTbl->CheckTable() == 0);
  728. #ifdef LKR_DEPRECATED_ITERATORS
  729. IRTLTRACE0("Checking iterators\n");
  730. cRec = 0;
  731. CWordHash::CIterator iter(LKL_READLOCK);
  732. for (lkrc = pTbl->InitializeIterator(&iter);
  733. lkrc == LK_SUCCESS;
  734. lkrc = pTbl->IncrementIterator(&iter))
  735. {
  736. ++cRec;
  737. const CStr* pstrKey = iter.Key();
  738. CWord* pRec = iter.Record();
  739. IRTLASSERT(&g_wordtable[0] <= pRec && pRec < &g_wordtable[g_nokeys]);
  740. IRTLASSERT(!pRec->m_fIterated);
  741. pRec->m_fIterated = true;
  742. if (CWordHash::TableLock::Recursion() != LOCK_NON_RECURSIVE
  743. && CWordHash::BucketLock::Recursion() != LOCK_NON_RECURSIVE)
  744. {
  745. // Check that the lock can be safely acquired recursively
  746. // (the table is already locked by the iterator).
  747. int x = rand() % g_nokeys;
  748. CStr* pstrKey2 = &g_wordtable[x].m_str;
  749. CWord* pRec2 = NULL;
  750. LK_RETCODE lkrc2= pTbl->FindKey(pstrKey2, &pRec2);
  751. IRTLASSERT(lkrc2 == LK_SUCCESS && pRec2 == &g_wordtable[x]);
  752. if (pRec2 != NULL)
  753. --pRec2->m_cRefs;
  754. }
  755. }
  756. IRTLASSERT(lkrc == LK_NO_MORE_ELEMENTS);
  757. IRTLASSERT((int) cRec == g_nokeys);
  758. lkrc = pTbl->CloseIterator(&iter);
  759. IRTLASSERT(lkrc == LK_SUCCESS);
  760. for (i = 0 ; i < g_nokeys ; i++ )
  761. {
  762. IRTLASSERT(g_wordtable[i].m_fIterated);
  763. g_wordtable[i].m_fIterated = false;
  764. }
  765. do {
  766. cRec = rand() % (g_nokeys - 1);
  767. } while (cRec == 0);
  768. IRTLTRACE1("Checking abandoning of const iterators after %d iterations\n",
  769. cRec);
  770. const CWordHash *pTblConst = pTbl;
  771. CWordHash::CConstIterator iterConst;
  772. for (lkrc = pTblConst->InitializeIterator(&iterConst);
  773. lkrc == LK_SUCCESS;
  774. lkrc = pTblConst->IncrementIterator(&iterConst))
  775. {
  776. if (--cRec == 0)
  777. break;
  778. const CStr* pszKey = iterConst.Key();
  779. const CWord* pRec = iterConst.Record();
  780. IRTLASSERT(&g_wordtable[0] <= pRec && pRec < &g_wordtable[g_nokeys]);
  781. }
  782. IRTLASSERT(lkrc != LK_NO_MORE_ELEMENTS);
  783. lkrc = pTblConst->CloseIterator(&iterConst);
  784. IRTLASSERT(lkrc == LK_SUCCESS);
  785. #endif // LKR_DEPRECATED_ITERATORS
  786. #ifndef LKRHASH_KERNEL_MODE
  787. IRTLTRACE0("Gathering statistics\n");
  788. CLKRHashTableStats stats = pTbl->GetStatistics();
  789. print_table_statistics(stats);
  790. #endif // !LKRHASH_KERNEL_MODE
  791. #ifdef LOCK_INSTRUMENTATION
  792. print_lock_statistics(stats);
  793. CWordHash::BucketLock::ResetGlobalStatistics();
  794. CWordHash::TableLock::ResetGlobalStatistics();
  795. #endif
  796. _tprintf(_TEXT("\n"));
  797. IRTLTRACE0("Cleaning up by hand\n");
  798. for (i = 0 ; i < g_nokeys ; i++ )
  799. {
  800. IRTLVERIFY(pTbl->DeleteKey(&g_wordtable[i].m_str) == LK_SUCCESS);
  801. }
  802. IRTLASSERT(0 == pTbl->Size());
  803. delete pTbl ;
  804. }
  805. #ifdef LKR_STL_ITERATORS
  806. void test_stl_iterators2(
  807. CWordHash *pTbl);
  808. void test_stl_iterators(
  809. double highload,
  810. int initsize,
  811. int nsubtbls)
  812. {
  813. _tprintf(_TEXT("\nTesting STL iterators...\n"));
  814. _tprintf(_TEXT("subtable iter = %d, iter = %d\n"),
  815. sizeof(CLKRLinearHashTable::Iterator),
  816. sizeof(CLKRHashTable::Iterator));
  817. int i;
  818. bool f;
  819. CWordHash *pTbl;
  820. CWordHash::iterator iter;
  821. const int iFirst = 5; // g_nokeys / 5;
  822. const int iLast = 10; // 4 * g_nokeys / 5;
  823. // pTbl = new CWordHash(highload, initsize, nsubtbls) ;
  824. IRTLTRACE1("\n\nAbout to create table with %d records\n\n",
  825. iLast - iFirst);
  826. pTbl = new CWordHash(&g_wordtable[iFirst], &g_wordtable[iLast],
  827. highload, initsize, nsubtbls) ;
  828. for (iter = pTbl->begin(); iter != pTbl->end(); ++iter)
  829. {
  830. const CStr* pstrKey = iter.Key();
  831. CWord* pRec = iter.Record();
  832. IRTLASSERT(&g_wordtable[iFirst] <= pRec
  833. && pRec < &g_wordtable[iLast]);
  834. IRTLTRACE2("\nRecord: %p, %hs\n", pRec, pstrKey->m_psz);
  835. }
  836. IRTLTRACE1("\n\nAbout to search %d records\n\n", pTbl->Size());
  837. for (i = iFirst; i != iLast; ++i)
  838. {
  839. f = pTbl->Find(&g_wordtable[i].m_str, iter);
  840. IRTLASSERT(f && iter.Record() == &g_wordtable[i]);
  841. IRTLTRACE2("\n\tFound: %d, %hs\n", i, iter.Key()->m_psz);
  842. }
  843. f = pTbl->Find(&g_wordtable[iLast].m_str, iter);
  844. IRTLASSERT(!f);
  845. IRTLASSERT(iter == pTbl->end());
  846. i = pTbl->Size();
  847. IRTLTRACE1("\n\nAbout to erase %d records\n\n", i);
  848. for (iter = pTbl->begin(); iter != pTbl->end(); --i)
  849. {
  850. IRTLTRACE1("\n\terase %d\n", i);
  851. IRTLVERIFY(pTbl->Erase(iter));
  852. }
  853. IRTLASSERT(i == 0);
  854. IRTLASSERT(pTbl->Size() == 0);
  855. CheckRefCounts(0);
  856. IRTLTRACE1("\n\nAbout to insert %d records\n\n", iLast - iFirst);
  857. for (i = iFirst; i != iLast; ++i)
  858. {
  859. f = pTbl->Insert(&g_wordtable[i], iter);
  860. IRTLASSERT(f && iter.Record() == &g_wordtable[i]);
  861. IRTLTRACE2("\n\tInserted: %d, %hs\n", i, iter.Key()->m_psz);
  862. }
  863. // Reset iter so that it isn't pointing to anything, raising its refcount
  864. iter = pTbl->end();
  865. CheckRefCounts(1, iFirst, iLast);
  866. IRTLTRACE1("\n\nAbout to Erase2 %d records\n\n", iLast - iFirst);
  867. f = pTbl->Erase(pTbl->begin(), pTbl->end());
  868. IRTLASSERT(f && pTbl->Size() == 0);
  869. CheckRefCounts(0);
  870. IRTLTRACE1("\n\nAbout to insert %d records, again\n\n", iLast - iFirst);
  871. for (i = iFirst; i != iLast; ++i)
  872. {
  873. f = pTbl->Insert(&g_wordtable[i], iter);
  874. IRTLASSERT(f && iter.Record() == &g_wordtable[i]);
  875. IRTLTRACE2("\n\tInserted: %d, %hs\n", i, iter.Key()->m_psz);
  876. }
  877. // Reset iter so that it isn't pointing to anything, raising its refcount
  878. iter = pTbl->end();
  879. CheckRefCounts(1, iFirst, iLast);
  880. IRTLTRACE1("\nAbout to equalrange and erase2 %d records, backwards\n\n",
  881. iLast - iFirst);
  882. for (i = iLast; --i >= iFirst; )
  883. {
  884. CWordHash::iterator iterLast;
  885. f = pTbl->EqualRange(&g_wordtable[i].m_str, iter, iterLast);
  886. IRTLASSERT(f && iter.Record() == &g_wordtable[i]);
  887. IRTLTRACE3("\n\tEqualRange: %d, \"%hs\", %d\n",
  888. i, iter.Key()->m_psz, iter.Record()->m_cRefs);
  889. f = pTbl->Erase(iter, iterLast);
  890. IRTLASSERT(f);
  891. IRTLTRACE1("\n\tErase2d: %d\n", i);
  892. }
  893. IRTLASSERT(pTbl->Size() == 0);
  894. CheckRefCounts(0);
  895. delete pTbl;
  896. #if 1
  897. pTbl = new CWordHash(highload, initsize, nsubtbls) ;
  898. #else
  899. pTbl = new CWordHash(1, // LK_DFLT_MAXLOAD * 6,
  900. 100000, // LK_SMALL_TABLESIZE,
  901. 17); // # subtables
  902. #endif
  903. CheckRefCounts(0);
  904. IRTLTRACE0("Building the table\n");
  905. for (i = 0 ; i < g_nokeys ; i++ )
  906. {
  907. g_wordtable[i].m_fIterated = false;
  908. IRTLVERIFY(pTbl->InsertRecord(&g_wordtable[i]) == LK_SUCCESS);
  909. }
  910. IRTLASSERT(g_nokeys == (int) pTbl->Size());
  911. IRTLASSERT(pTbl->CheckTable() == 0);
  912. test_stl_iterators2(pTbl);
  913. IRTLTRACE0("Cleaning up by hand\n");
  914. for (i = 0 ; i < g_nokeys ; i++ )
  915. {
  916. IRTLVERIFY(pTbl->DeleteKey(&g_wordtable[i].m_str) == LK_SUCCESS);
  917. }
  918. IRTLASSERT(0 == pTbl->Size());
  919. delete pTbl ;
  920. }
  921. void test_stl_iterators2(
  922. CWordHash *pTbl)
  923. {
  924. IRTLTRACE0("Checking STL iterators\n");
  925. size_t cRec = 0;
  926. int i;
  927. for (CWordHash::iterator iter = pTbl->begin();
  928. iter != pTbl->end();
  929. ++iter)
  930. {
  931. ++cRec;
  932. const CStr* pstrKey = iter.Key();
  933. CWord* pRec = iter.Record();
  934. IRTLASSERT(&g_wordtable[0] <= pRec && pRec < &g_wordtable[g_nokeys]);
  935. IRTLASSERT(!pRec->m_fIterated);
  936. pRec->m_fIterated = true;
  937. // IRTLTRACE3("%d: %p, %hs\n", cRec, pRec, pstrKey->m_psz);
  938. }
  939. IRTLASSERT((int) cRec == g_nokeys);
  940. IRTLTRACE1("Checking that all %d records were touched\n", g_nokeys);
  941. CheckRefCounts(1);
  942. for (i = 0 ; i < g_nokeys ; i++ )
  943. {
  944. IRTLASSERT(g_wordtable[i].m_fIterated);
  945. g_wordtable[i].m_fIterated = false;
  946. }
  947. }
  948. #endif // LKR_STL_ITERATORS
  949. #ifndef LKRHASH_KERNEL_MODE
  950. void print_table_statistics(const CLKRHashTableStats& stats)
  951. {
  952. _tprintf(_TEXT("#Records=%d, #BucketChains=%d, ")
  953. _TEXT("DirSize=%d, LongestChain=%3d,\n"),
  954. stats.RecordCount, stats.TableSize,
  955. stats.DirectorySize, stats.LongestChain);
  956. _tprintf(_TEXT("#Empty Buckets=%d, Split Factor=%.2f, ")
  957. _TEXT("AvgSrchLen=%.2f, Expected SL=%.2f,\n"),
  958. stats.EmptySlots, stats.SplitFactor,
  959. stats.AvgSearchLength, stats.ExpSearchLength);
  960. _tprintf(_TEXT("Avg Unsuccessful SrchLen=%.2f, ExpUSL=%.2f.\n"),
  961. stats.AvgUSearchLength, stats.ExpUSearchLength);
  962. _tprintf(_TEXT("\nBucket Chain Lengths ")
  963. _TEXT("(node clump size = %d, bucket size = %d bytes):\n"),
  964. stats.NodeClumpSize, stats.CBucketSize);
  965. for (int j = 0; j < CLKRHashTableStats::MAX_BUCKETS; ++j)
  966. {
  967. if (stats.m_aBucketLenHistogram[j] == 0)
  968. {
  969. _tprintf(_TEXT("\n"));
  970. break;
  971. }
  972. _tprintf(_TEXT(" %10d: %6d"),
  973. stats.BucketSize(j), stats.m_aBucketLenHistogram[j]);
  974. if (j % 4 == 3)
  975. _tprintf(_TEXT("\n"));
  976. }
  977. _tprintf(_TEXT("\n"));
  978. }
  979. #ifdef LOCK_INSTRUMENTATION
  980. void print_lock_statistics(const CLKRHashTableStats& stats)
  981. {
  982. _tprintf(_TEXT("Global Locks Statistics:")
  983. _TEXT("\n total locks created = %ld, ")
  984. _TEXT("total contentions = %ld, ")
  985. _TEXT("#sleeps = %ld,")
  986. _TEXT("\n total spins = %I64d, ")
  987. _TEXT("av spins/contention = %.1f, ")
  988. _TEXT("\n #readlocks = %d, ")
  989. _TEXT("#writelocks=%d\n"),
  990. stats.m_gls.m_cTotalLocks,
  991. stats.m_gls.m_cContendedLocks,
  992. stats.m_gls.m_nSleeps,
  993. stats.m_gls.m_cTotalSpins,
  994. stats.m_gls.m_nAverageSpins,
  995. stats.m_gls.m_nReadLocks,
  996. stats.m_gls.m_nWriteLocks
  997. );
  998. _tprintf(_TEXT("Averaged SubTable Locks Statistics:")
  999. _TEXT("\n Total locks = %d, ")
  1000. _TEXT("#contentions = %.1f, ")
  1001. _TEXT("sleeps = %.1f; ")
  1002. _TEXT("\n total spins = %.1f, ")
  1003. _TEXT("avg spins = %.1f, ")
  1004. _TEXT("\n #readlocks = %.1f, ")
  1005. _TEXT("#writelocks=%.1f\n"),
  1006. stats.m_alsTable.m_nItems,
  1007. stats.m_alsTable.m_nContentions,
  1008. stats.m_alsTable.m_nSleeps,
  1009. stats.m_alsTable.m_nContentionSpins,
  1010. stats.m_alsTable.m_nAverageSpins,
  1011. stats.m_alsTable.m_nReadLocks,
  1012. stats.m_alsTable.m_nWriteLocks);
  1013. _tprintf(_TEXT("Averaged Bucket Locks Statistics:")
  1014. _TEXT("\n Total locks = %d, ")
  1015. _TEXT("#contentions = %.1f, ")
  1016. _TEXT("sleeps = %.1f; ")
  1017. _TEXT("\n total spins = %.1f, ")
  1018. _TEXT("avg spins = %.1f, ")
  1019. _TEXT("\n #readlocks = %.1f, ")
  1020. _TEXT("#writelocks=%.1f\n"),
  1021. stats.m_alsBucketsAvg.m_nItems,
  1022. stats.m_alsBucketsAvg.m_nContentions,
  1023. stats.m_alsBucketsAvg.m_nSleeps,
  1024. stats.m_alsBucketsAvg.m_nContentionSpins,
  1025. stats.m_alsBucketsAvg.m_nAverageSpins,
  1026. stats.m_alsBucketsAvg.m_nReadLocks,
  1027. stats.m_alsBucketsAvg.m_nWriteLocks);
  1028. _tprintf(_TEXT("\n"));
  1029. }
  1030. #endif // LOCK_INSTRUMENTATION
  1031. #endif // !LKRHASH_KERNEL_MODE
  1032. int expand_key_set(int maxkeys, int numkeys, bool fVerbose)
  1033. {
  1034. int totkeys = numkeys ;
  1035. if (totkeys > maxkeys)
  1036. return maxkeys;
  1037. char* pszTemp = new char [20 + CStr::sm_cchMax];
  1038. for(int k = 0; TRUE; k++)
  1039. {
  1040. for(int i = 0; i < numkeys; i++)
  1041. {
  1042. if (totkeys == maxkeys)
  1043. {
  1044. delete [] pszTemp;
  1045. return(totkeys) ;
  1046. }
  1047. sprintf(pszTemp, "%d%hs", k, g_wordtable[i].m_str.m_psz);
  1048. g_wordtable[totkeys++].m_str.Set(pszTemp, strlen(pszTemp));
  1049. }
  1050. if (fVerbose)
  1051. putchar('.');
  1052. }
  1053. }
  1054. #ifdef LKRHASH_KERNEL_MODE
  1055. void
  1056. #else
  1057. unsigned __stdcall
  1058. #endif
  1059. exercise_table(
  1060. void* pinput)
  1061. {
  1062. CWordHash* pTbl;
  1063. thread_data* pdea = (thread_data*) pinput ;
  1064. int cfailed_ins=0 ;
  1065. int cfailed_dels=0 ;
  1066. int cFoundSuccesses=0, cFoundFails=0 ;
  1067. int x, i ;
  1068. LK_RETCODE lkrc;
  1069. SetThreadIdealProcessor(GetCurrentThread(),
  1070. pdea->threadno % NumProcessors());
  1071. #ifndef LKRHASH_KERNEL_MODE
  1072. LARGE_INTEGER liFreq, liT1, liT2;
  1073. IRTLVERIFY(QueryPerformanceFrequency(&liFreq));
  1074. IRTLVERIFY(QueryPerformanceCounter(&liT1));
  1075. #endif // !LKRHASH_KERNEL_MODE
  1076. pdea->cinserts = 0 ;
  1077. pdea->cdeletes = 0 ;
  1078. pdea->clookups = 0 ;
  1079. pTbl = pdea->ptbl ;
  1080. srand(pdea->m_nSeed);
  1081. for (int rnd = 0; rnd < pdea->rounds; rnd++)
  1082. {
  1083. IRTLASSERT(pTbl->CheckTable() == 0);
  1084. // Insert all the keys, randomly searching after each insertion
  1085. for (i = pdea->first_key ; i < pdea->last_key ; i++ )
  1086. {
  1087. #ifdef IRTLDEBUG
  1088. CStr* pstrKey = &g_wordtable[i].m_str;
  1089. CWord* pRec = NULL;
  1090. lkrc = pTbl->FindKey(pstrKey, &pRec);
  1091. IRTLASSERT(lkrc == LK_NO_SUCH_KEY && pRec == NULL);
  1092. #endif // IRTLDEBUG
  1093. if (pTbl->InsertRecord(&g_wordtable[i] ) != LK_SUCCESS )
  1094. {
  1095. cfailed_ins++ ;
  1096. }
  1097. else
  1098. {
  1099. #ifdef IRTLDEBUG
  1100. pstrKey = &g_wordtable[i].m_str;
  1101. lkrc = pTbl->FindKey(pstrKey, &pRec);
  1102. IRTLASSERT(lkrc == LK_SUCCESS && pRec == &g_wordtable[i]);
  1103. pTbl->AddRefRecord(pRec, LKAR_EXPLICIT_RELEASE);
  1104. #endif // IRTLDEBUG
  1105. g_wordtable[i].m_fInserted = true;
  1106. }
  1107. pdea->cinserts++ ;
  1108. for (int lu = 0; lu < pdea->lookup_freq; lu++)
  1109. {
  1110. x = rand() % (pdea->last_key - pdea->first_key)
  1111. + pdea->first_key;
  1112. bool fPresent = (x <= i); // should it be found?
  1113. CWord* pRec = NULL;
  1114. LK_RETCODE lkrc;
  1115. if (pdea->m_nFindKeyCopy > 0
  1116. && rand() % pdea->m_nFindKeyCopy == 0)
  1117. {
  1118. char szTemp[MAX_STRSIZE];
  1119. strcpy(szTemp, g_wordtable[x].m_str.m_psz);
  1120. CStr strTemp(szTemp, g_wordtable[x].m_str.m_cch, false);
  1121. lkrc = pTbl->FindKey(&strTemp, &pRec);
  1122. }
  1123. else
  1124. lkrc = pTbl->FindKey(&g_wordtable[x].m_str, &pRec);
  1125. if (fPresent)
  1126. {
  1127. if (lkrc != LK_SUCCESS || pRec != &g_wordtable[x] )
  1128. {
  1129. ++g_wordtable[x].m_cNotFound;
  1130. IRTLTRACE(_TEXT("%d: Not found (%hs): x = %d, i = %d, ")
  1131. _TEXT("cnf = %d, rnd = %d, lkrc = %d, ")
  1132. _TEXT("pRec(%hs), %d\n"),
  1133. pdea->threadno, g_wordtable[x].m_str.m_psz, x, i,
  1134. g_wordtable[x].m_cNotFound, rnd, lkrc,
  1135. pRec != NULL ? pRec->m_str.m_psz : "<null>",
  1136. pRec != NULL ? (pRec - g_wordtable) / sizeof(CWord) : -1);
  1137. cFoundFails++ ;
  1138. }
  1139. else
  1140. {
  1141. pTbl->AddRefRecord(pRec, LKAR_EXPLICIT_RELEASE);
  1142. cFoundSuccesses++ ;
  1143. }
  1144. }
  1145. else // not fPresent
  1146. {
  1147. IRTLASSERT(lkrc != LK_SUCCESS && pRec == NULL);
  1148. if (lkrc == LK_SUCCESS || pRec != NULL)
  1149. {
  1150. IRTLTRACE(_TEXT("%d: found when not present (%hs): ")
  1151. _TEXT("x = %d, i = %d, ")
  1152. _TEXT("cnf = %d, rnd = %d, lkrc = %d, ")
  1153. _TEXT("pRec(%hs), %d\n"),
  1154. pdea->threadno, g_wordtable[x].m_str.m_psz,
  1155. x, i,
  1156. g_wordtable[x].m_cNotFound, rnd, lkrc,
  1157. pRec != NULL ? pRec->m_str.m_psz : "<null>",
  1158. pRec != NULL ? (pRec - g_wordtable) / sizeof(CWord) : -1);
  1159. cFoundFails++ ;
  1160. }
  1161. else
  1162. {
  1163. // wasn't found, but it wasn't present, so this is good
  1164. cFoundSuccesses++ ;
  1165. }
  1166. }
  1167. }
  1168. pdea->clookups += pdea->lookup_freq ;
  1169. #ifdef LKR_EXPOSED_TABLE_LOCK
  1170. if (pdea->m_nInsertIfNotFound > 0
  1171. && rand() % pdea->m_nInsertIfNotFound == 0)
  1172. {
  1173. bool fWrite = (rand() & 1) != 0;
  1174. if (fWrite)
  1175. pTbl->WriteLock();
  1176. else
  1177. pTbl->ReadLock();
  1178. x = rand() % (pdea->last_key - pdea->first_key)
  1179. + pdea->first_key;
  1180. bool fPresent = (x <= i); // should it be found?
  1181. CStr* pstrKey = &g_wordtable[x].m_str;
  1182. CWord* pRec = NULL;
  1183. lkrc = pTbl->FindKey(pstrKey, &pRec);
  1184. if (pRec != NULL)
  1185. {
  1186. IRTLASSERT(lkrc == LK_SUCCESS);
  1187. IRTLASSERT(pRec == &g_wordtable[x]);
  1188. IRTLASSERT(x <= i);
  1189. pTbl->AddRefRecord(pRec, LKAR_EXPLICIT_RELEASE);
  1190. }
  1191. else if (fWrite)
  1192. {
  1193. IRTLASSERT(x > i);
  1194. IRTLASSERT(lkrc == LK_NO_SUCH_KEY);
  1195. lkrc = pTbl->InsertRecord(&g_wordtable[x], false);
  1196. IRTLASSERT(lkrc == LK_SUCCESS);
  1197. InterlockedIncrement(&g_wordtable[x].m_cInsertIfNotFounds);
  1198. lkrc = pTbl->DeleteKey(&g_wordtable[x].m_str);
  1199. IRTLASSERT(lkrc == LK_SUCCESS);
  1200. }
  1201. if (fWrite)
  1202. pTbl->WriteUnlock();
  1203. else
  1204. pTbl->ReadUnlock();
  1205. }
  1206. #endif // LKR_EXPOSED_TABLE_LOCK
  1207. }
  1208. IRTLASSERT(cfailed_ins == 0) ;
  1209. IRTLASSERT(cFoundFails == 0) ;
  1210. IRTLASSERT(cFoundSuccesses == ((2 * rnd + 1) * pdea->lookup_freq
  1211. * (pdea->last_key - pdea->first_key)));
  1212. IRTLTRACE(_TEXT("Thrd %u, rnd %d: %d inserts done, not found %d, ")
  1213. _TEXT("f=%d, l=%d\n"),
  1214. pdea->threadno, rnd, pdea->cinserts, cFoundFails,
  1215. pdea->first_key, pdea->last_key) ;
  1216. IRTLASSERT(pTbl->CheckTable() == 0);
  1217. // Delete all the keys, randomly searching before each deletion
  1218. for (i = pdea->first_key ; i < pdea->last_key ; i++ )
  1219. {
  1220. for (int lu = 0; lu < pdea->lookup_freq; lu++)
  1221. {
  1222. x = rand() % (pdea->last_key - pdea->first_key)
  1223. + pdea->first_key;
  1224. bool fPresent = (x >= i); // should it be found?
  1225. CWord* pRec = NULL;
  1226. LK_RETCODE lkrc;
  1227. if (pdea->m_nFindKeyCopy > 0
  1228. && rand() % pdea->m_nFindKeyCopy == 0)
  1229. {
  1230. char szTemp[MAX_STRSIZE];
  1231. strcpy(szTemp, g_wordtable[x].m_str.m_psz);
  1232. CStr strTemp(szTemp, g_wordtable[x].m_str.m_cch, false);
  1233. lkrc = pTbl->FindKey(&strTemp, &pRec);
  1234. }
  1235. else
  1236. lkrc = pTbl->FindKey(&g_wordtable[x].m_str, &pRec);
  1237. if (fPresent)
  1238. {
  1239. if (lkrc != LK_SUCCESS || pRec != &g_wordtable[x] )
  1240. {
  1241. ++g_wordtable[x].m_cNotFound;
  1242. IRTLTRACE(_TEXT("%d: Not found (%hs): x = %d, i = %d, ")
  1243. _TEXT("cnf = %d, rnd = %d, lkrc = %d, ")
  1244. _TEXT("pRec(%hs), %d\n"),
  1245. pdea->threadno, g_wordtable[x].m_str.m_psz, x, i,
  1246. g_wordtable[x].m_cNotFound, rnd, lkrc,
  1247. pRec != NULL ? pRec->m_str.m_psz : "<null>",
  1248. pRec != NULL ? (pRec - g_wordtable) / sizeof(CWord) : -1);
  1249. cFoundFails++ ;
  1250. }
  1251. else
  1252. {
  1253. pTbl->AddRefRecord(pRec, LKAR_EXPLICIT_RELEASE);
  1254. cFoundSuccesses++ ;
  1255. }
  1256. }
  1257. else // !fPresent
  1258. {
  1259. IRTLASSERT(lkrc != LK_SUCCESS && pRec == NULL);
  1260. if (lkrc == LK_SUCCESS || pRec != NULL)
  1261. {
  1262. IRTLTRACE(_TEXT("%d: found when not present (%hs): ")
  1263. _TEXT("x = %d, i = %d, ")
  1264. _TEXT("cnf = %d, rnd = %d, lkrc = %d, ")
  1265. _TEXT("pRec(%hs), %d\n"),
  1266. pdea->threadno, g_wordtable[x].m_str.m_psz,
  1267. x, i,
  1268. g_wordtable[x].m_cNotFound, rnd, lkrc,
  1269. pRec != NULL ? pRec->m_str.m_psz : "<null>",
  1270. pRec != NULL ? (pRec - g_wordtable) / sizeof(CWord) : -1);
  1271. cFoundFails++ ;
  1272. }
  1273. else
  1274. {
  1275. // wasn't found, but it wasn't present, so this is good
  1276. cFoundSuccesses++ ;
  1277. }
  1278. }
  1279. }
  1280. pdea->clookups += pdea->lookup_freq ;
  1281. #ifdef IRTLDEBUG
  1282. CStr* pstrKey = &g_wordtable[i].m_str;
  1283. CWord* pRec = NULL;
  1284. LK_RETCODE lkrc = pTbl->FindKey(pstrKey, &pRec);
  1285. IRTLASSERT(lkrc == LK_SUCCESS && pRec == &g_wordtable[i]);
  1286. pTbl->AddRefRecord(pRec, LKAR_EXPLICIT_RELEASE);
  1287. #endif // IRTLDEBUG
  1288. if (pTbl->DeleteKey(&g_wordtable[i].m_str) != LK_SUCCESS )
  1289. {
  1290. cfailed_dels++ ;
  1291. }
  1292. else
  1293. {
  1294. #ifdef IRTLDEBUG
  1295. pstrKey = &g_wordtable[i].m_str;
  1296. lkrc = pTbl->FindKey(pstrKey, &pRec);
  1297. IRTLASSERT(lkrc == LK_NO_SUCH_KEY && pRec == NULL);
  1298. #endif // IRTLDEBUG
  1299. g_wordtable[i].m_fInserted = false;
  1300. }
  1301. pdea->cdeletes++ ;
  1302. }
  1303. #ifdef IRTLDEBUG
  1304. int cBadKeys = 0;
  1305. for (i = pdea->first_key ; i < pdea->last_key ; i++ )
  1306. {
  1307. if (g_wordtable[i].m_cNotFound > 0)
  1308. {
  1309. ++cBadKeys;
  1310. IRTLTRACE(_TEXT("%-20hs: #not found = %d, hash = %d, %08x\n"),
  1311. g_wordtable[i].m_str.m_psz,
  1312. g_wordtable[i].m_cNotFound,
  1313. CWordHash::CalcKeyHash(CWordHash::ExtractKey(
  1314. &g_wordtable[i])),
  1315. CWordHash::CalcKeyHash(CWordHash::ExtractKey(
  1316. &g_wordtable[i])));
  1317. }
  1318. }
  1319. if (cBadKeys > 0)
  1320. IRTLTRACE1("%d bad keys\n", cBadKeys);
  1321. IRTLASSERT(cBadKeys == 0);
  1322. #endif // IRTLDEBUG
  1323. IRTLASSERT(cfailed_dels == 0 ) ;
  1324. IRTLASSERT(cFoundFails == 0 ) ;
  1325. IRTLASSERT(cFoundSuccesses == ((2 * rnd + 2) * pdea->lookup_freq
  1326. * (pdea->last_key - pdea->first_key)));
  1327. IRTLTRACE(_TEXT("Thrd %u, rnd %d: %d deletes done, not found %d, ")
  1328. _TEXT("f=%d, l=%d\n"),
  1329. pdea->threadno, rnd, pdea->cdeletes, cFoundFails,
  1330. pdea->first_key, pdea->last_key) ;
  1331. } // (for rnd)
  1332. #ifndef LKRHASH_KERNEL_MODE
  1333. IRTLVERIFY(QueryPerformanceCounter(&liT2));
  1334. pdea->duration = (liT2.QuadPart-liT1.QuadPart) / (double) liFreq.QuadPart;
  1335. #endif // !LKRHASH_KERNEL_MODE
  1336. IRTLASSERT(pTbl->CheckTable() == 0);
  1337. IRTLTRACE3("Thread %u terminating: %d found, %d not found\n",
  1338. pdea->threadno, cFoundSuccesses, cFoundFails) ;
  1339. if (cFoundSuccesses != (2 * pdea->rounds * pdea->lookup_freq
  1340. * (pdea->last_key - pdea->first_key))
  1341. || cFoundFails != 0 || cfailed_ins != 0 || cfailed_dels != 0)
  1342. {
  1343. _tprintf(_TEXT("Thread %u: found = %d, not found = %d, ")
  1344. _TEXT("\nfailed inserts = %d, failed deletes = %d\n"),
  1345. pdea->threadno, cFoundSuccesses, cFoundFails,
  1346. cfailed_ins, cfailed_dels);
  1347. }
  1348. pdea->cfailures = cfailed_ins + cfailed_dels + cFoundFails;
  1349. if (pdea->hevFinished != NULL)
  1350. SetEvent(pdea->hevFinished);
  1351. #ifndef LKRHASH_KERNEL_MODE
  1352. return 0;
  1353. #endif
  1354. }