Leaked source code of windows server 2003
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.

1759 lines
53 KiB

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