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.

1265 lines
38 KiB

  1. /* Test driver for class HashTable */
  2. /* Author: Paul Larson, [email protected] */
  3. /* Much hacked upon by George V. Reilly, [email protected] */
  4. #include <acache.hxx>
  5. #include <windows.h>
  6. #include <dbgutil.h>
  7. #include <process.h>
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <io.h>
  11. #include <stddef.h>
  12. #include <conio.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. #include <time.h>
  16. #include <malloc.h>
  17. #define SAMPLE_LKRHASH_TESTCLASS
  18. // disable warning messages about truncating extremly long identifiers
  19. // #pragma warning (disable : 4786)
  20. #include <lkrhash.h>
  21. #define MAXKEYS 1000000
  22. #define MAX_THREADS MAXIMUM_WAIT_OBJECTS
  23. DECLARE_DEBUG_PRINTS_OBJECT();
  24. void test_iterators(double highload, int initsize, int nsubtbls);
  25. void print_table_statistics(const CLKRHashTableStats& stats);
  26. #ifdef LOCK_INSTRUMENTATION
  27. void print_lock_statistics(const CLKRHashTableStats &stats);
  28. #endif
  29. int expand_key_set(int maxkeys, int numkeys, bool fVerbose) ;
  30. void __cdecl exercise_table(void *pinput) ;
  31. // A string wrapper class that keeps track of the length of the string data.
  32. // A more useful string class would refcount the data and have copy-on-write
  33. // semantics (or use MFC's CString or STL's string classes).
  34. class CStr
  35. {
  36. public:
  37. const char* m_psz;
  38. int m_cch;
  39. static int sm_cchMax;
  40. CStr()
  41. : m_psz(NULL),
  42. m_cch(0)
  43. {
  44. }
  45. void Set(const char* psz)
  46. {
  47. free((void*) m_psz);
  48. m_psz = _strdup(psz);
  49. m_cch = strlen(m_psz);
  50. sm_cchMax = max(m_cch, sm_cchMax);
  51. }
  52. ~CStr()
  53. {
  54. free((void*) m_psz);
  55. }
  56. };
  57. // length of longest string seen
  58. int CStr::sm_cchMax = 0;
  59. // a word from the data file, which contains one 'word' per line (may
  60. // include spaces).
  61. class CWord
  62. {
  63. public:
  64. int m_cNotFound;
  65. CStr m_str;
  66. bool m_fInserted;
  67. bool m_fIterated;
  68. LONG m_cRefs;
  69. CWord()
  70. : m_cNotFound(0),
  71. m_fInserted(false),
  72. m_fIterated(false),
  73. m_cRefs(0)
  74. {
  75. }
  76. ~CWord()
  77. {
  78. IRTLASSERT(m_cRefs == 0);
  79. #ifdef _DEBUG
  80. if (m_cRefs != 0)
  81. TRACE("%s: %d\n", m_str.m_psz, m_cRefs);
  82. #endif
  83. }
  84. };
  85. // A hash table of CWords, indexed by CStr*s.
  86. class CWordHash
  87. : public CTypedHashTable<CWordHash, CWord, const CStr*>
  88. {
  89. public:
  90. static bool sm_fCaseInsensitive;
  91. static int sm_nLastChars;
  92. static const CStr*
  93. ExtractKey(const CWord* pKey)
  94. {
  95. return &pKey->m_str;
  96. }
  97. static DWORD
  98. CalcKeyHash(const CStr* pstrKey)
  99. {
  100. const char* psz = pstrKey->m_psz;
  101. // use only the last few chars instead of whole string?
  102. if (sm_nLastChars > 0 && pstrKey->m_cch >= sm_nLastChars)
  103. psz = pstrKey->m_psz + pstrKey->m_cch - sm_nLastChars;
  104. IRTLASSERT(pstrKey->m_psz <= psz
  105. && psz < pstrKey->m_psz + pstrKey->m_cch);
  106. if (sm_fCaseInsensitive)
  107. return HashStringNoCase(psz, pstrKey->m_cch);
  108. else
  109. return HashString(psz, pstrKey->m_cch);
  110. }
  111. static bool
  112. EqualKeys(const CStr* pstrKey1, const CStr* pstrKey2)
  113. {
  114. if (pstrKey1->m_cch != pstrKey2->m_cch)
  115. return false;
  116. else if (sm_fCaseInsensitive)
  117. return ((pstrKey1->m_psz[0] & 0xDF) == (pstrKey2->m_psz[0] & 0xDF)
  118. && _stricmp(pstrKey1->m_psz, pstrKey2->m_psz) == 0);
  119. else
  120. return (pstrKey1->m_psz[0] == pstrKey2->m_psz[0]
  121. && strcmp(pstrKey1->m_psz, pstrKey2->m_psz) == 0);
  122. }
  123. static void
  124. AddRefRecord(CWord* pKey, int nIncr)
  125. {
  126. pKey->m_cRefs += nIncr;
  127. }
  128. CWordHash(
  129. double maxload, // Bound on avg chain length
  130. size_t initsize, // Initial size of hash table.
  131. size_t num_subtbls // #subordinate hash tables.
  132. )
  133. : CTypedHashTable<CWordHash, CWord, const CStr*>
  134. ("wordhash", maxload, initsize, num_subtbls)
  135. {}
  136. static const char*
  137. HashMethod()
  138. {
  139. char szLast[20];
  140. static char s_sz[50];
  141. if (sm_nLastChars > 0)
  142. sprintf(szLast, "last %d", sm_nLastChars);
  143. else
  144. strcpy(szLast, "all");
  145. sprintf(s_sz, "case-%ssensitive, %s chars",
  146. sm_fCaseInsensitive ? "in" : "", szLast);
  147. return s_sz;
  148. }
  149. };
  150. bool CWordHash::sm_fCaseInsensitive = true;
  151. int CWordHash::sm_nLastChars = 16;
  152. // globals
  153. int g_nokeys=0 ;
  154. CWord g_wordtable[MAXKEYS];
  155. struct thread_data
  156. {
  157. CWordHash* ptbl ;
  158. int threadno ;
  159. int first_key ;
  160. int last_key ;
  161. int rounds ;
  162. int lookup_freq ;
  163. float highload ;
  164. int cinserts ;
  165. int cdeletes ;
  166. int clookups ;
  167. int cfailures ;
  168. double duration ;
  169. HANDLE hevFinished;
  170. } ;
  171. class CFoo {
  172. public:
  173. BYTE m_b;
  174. char sz[12];
  175. };
  176. class CFooHashTable
  177. : public CTypedHashTable<CFooHashTable, const CFoo, BYTE>
  178. {
  179. public:
  180. CFooHashTable()
  181. : CTypedHashTable<CFooHashTable, const CFoo, BYTE>("foo") {}
  182. static BYTE ExtractKey(const CFoo* pFoo) {return pFoo->m_b;}
  183. static DWORD CalcKeyHash(BYTE nKey) {return nKey;}
  184. static bool EqualKeys(BYTE nKey1, BYTE nKey2) {return nKey1 == nKey2;}
  185. static void AddRefRecord(const CFoo* pFoo, int nIncr) {/* do nothing*/}
  186. };
  187. void
  188. InsertFoo()
  189. {
  190. CFoo foo;
  191. const CFoo *pfoo;
  192. CFooHashTable foohash;
  193. foohash.InsertRecord(&foo);
  194. foohash.FindKey(7, &pfoo);
  195. void* pv = &foohash;
  196. BYTE b = reinterpret_cast<BYTE>(pv);
  197. }
  198. class CIniFileSettings
  199. {
  200. public:
  201. CHAR m_szIniFile[MAX_PATH]; // the .ini file
  202. CHAR m_szDataFile[MAX_PATH]; // where string data table lives
  203. int m_nMaxKeys; // maximum number of keys
  204. double m_dblHighLoad; // maximum load of table (avg. bucket length)
  205. DWORD m_nInitSize; // initsize (1 => "small", 2 => "medium",
  206. // 3 => "large", other => exact)
  207. int m_nSubTables; // num subtables (0 => heuristic)
  208. int m_nLookupFreq; // lookup frequency
  209. int m_nMaxThreads; // max threads
  210. int m_nRounds; // num rounds
  211. int m_nSeed; // random seed
  212. bool m_fCaseInsensitive; // case-insensitive
  213. int m_nLastChars; // num last chars (0 => all chars)
  214. WORD m_wTableSpin; // table lock spin count (0 => no spinning on
  215. // MP machines)
  216. WORD m_wBucketSpin; // bucket lock spin count (0 => no MP spinning)
  217. double m_dblSpinAdjFctr; // spin adjustment factor
  218. bool m_fTestIterators; // run test_iterators?
  219. void ReadIniFile(LPCSTR pszIniFile);
  220. };
  221. void
  222. CIniFileSettings::ReadIniFile(
  223. LPCSTR pszIniFile)
  224. {
  225. strncpy(m_szIniFile, pszIniFile, MAX_PATH);
  226. TRACE("ReadIniFile(%s)\n", m_szIniFile);
  227. char szTemp[100];
  228. DWORD dwSize;
  229. const char szInvalidDefault[] = "??";
  230. dwSize = ::GetPrivateProfileString("HashTest", "DataFile",
  231. szInvalidDefault,
  232. m_szDataFile, MAX_PATH,
  233. m_szIniFile);
  234. TRACE("size = %d\n", dwSize);
  235. m_nMaxKeys = ::GetPrivateProfileInt("HashTest", "MaxKeys",
  236. MAXKEYS, m_szIniFile);
  237. m_nMaxKeys = max(1, m_nMaxKeys);
  238. m_nMaxKeys = min(m_nMaxKeys, MAXKEYS);
  239. dwSize = ::GetPrivateProfileString("HashTest", "MaxLoadFactor",
  240. "4", szTemp, sizeof(szTemp),
  241. m_szIniFile);
  242. sscanf(szTemp, "%lf", &m_dblHighLoad);
  243. m_dblHighLoad = max(1, m_dblHighLoad);
  244. m_nInitSize = ::GetPrivateProfileInt("HashTest", "InitSize",
  245. DFLT_LK_INITSIZE, m_szIniFile);
  246. m_nSubTables = ::GetPrivateProfileInt("HashTest", "NumSubtables",
  247. DFLT_LK_NUM_SUBTBLS, m_szIniFile);
  248. m_nLookupFreq = ::GetPrivateProfileInt("HashTest", "LookupFrequency",
  249. 5, m_szIniFile);
  250. m_nMaxThreads = ::GetPrivateProfileInt("HashTest", "MaxThreads",
  251. 1, m_szIniFile);
  252. m_nMaxThreads = min(MAX_THREADS, max(1, m_nMaxThreads));
  253. m_nRounds = ::GetPrivateProfileInt("HashTest", "NumRounds",
  254. 1, m_szIniFile);
  255. m_nRounds = max(1, m_nRounds);
  256. m_nSeed = ::GetPrivateProfileInt("HashTest", "RandomSeed",
  257. 1234, m_szIniFile);
  258. m_fCaseInsensitive = (::GetPrivateProfileInt("HashTest", "CaseInsensitive",
  259. 0, m_szIniFile) != 0);
  260. CWordHash::sm_fCaseInsensitive = m_fCaseInsensitive;
  261. m_nLastChars = ::GetPrivateProfileInt("HashTest", "NumLastChars",
  262. 0, m_szIniFile);
  263. CWordHash::sm_nLastChars = m_nLastChars;
  264. m_wTableSpin = (WORD) ::GetPrivateProfileInt("HashTest",
  265. "TableLockSpinCount",
  266. LOCK_DEFAULT_SPINS, m_szIniFile);
  267. m_wBucketSpin = (WORD) ::GetPrivateProfileInt("HashTest",
  268. "BucketLockSpinCount",
  269. LOCK_DEFAULT_SPINS, m_szIniFile);
  270. dwSize = ::GetPrivateProfileString("HashTest", "SpinAdjustmentFactor",
  271. "1.0", szTemp, sizeof(szTemp),
  272. m_szIniFile);
  273. sscanf(szTemp, "%lf", &m_dblSpinAdjFctr);
  274. #ifdef LKRHASH_GLOBAL_LOCK
  275. CWordHash::GlobalLock::SetDefaultSpinAdjustmentFactor(m_dblSpinAdjFctr);
  276. #endif
  277. CWordHash::TableLock::SetDefaultSpinAdjustmentFactor(m_dblSpinAdjFctr);
  278. CWordHash::BucketLock::SetDefaultSpinAdjustmentFactor(m_dblSpinAdjFctr);
  279. m_fTestIterators = (::GetPrivateProfileInt("HashTest", "TestIterators",
  280. 0, m_szIniFile) != 0);
  281. }
  282. const char*
  283. CommaNumber(
  284. int n,
  285. char* pszBuff)
  286. {
  287. char* psz = pszBuff;
  288. char chComma = '\0';
  289. int aThousands[4];
  290. int iThousands = 0;
  291. unsigned int u = n;
  292. if (n < 0)
  293. {
  294. *psz++ = '-';
  295. u = -n;
  296. }
  297. do {
  298. aThousands[iThousands++] = u % 1000;
  299. u /= 1000;
  300. } while (u != 0);
  301. while (--iThousands >= 0)
  302. {
  303. u = aThousands[iThousands];
  304. if (chComma)
  305. *psz++ = chComma;
  306. int d = u % 10;
  307. u /= 10;
  308. int t = u % 10;
  309. u /= 10;
  310. int h = u;
  311. if (h > 0 || chComma)
  312. *psz++ = h + '0';
  313. if (t > 0 || h > 0 || chComma)
  314. *psz++ = t + '0';
  315. *psz++ = d + '0';
  316. chComma = ',';
  317. }
  318. *psz = '\0';
  319. return pszBuff;
  320. }
  321. int __cdecl
  322. main(
  323. int argc,
  324. char **argv)
  325. {
  326. CWordHash *pTbl ;
  327. int num_threads ;
  328. thread_data de_area[MAX_THREADS] ;
  329. HANDLE ahEvents[MAX_THREADS];
  330. CIniFileSettings ifs;
  331. char sz[1024] ;
  332. FILE *fp ;
  333. int nodel=0;
  334. int keys_per_thread ;
  335. int i ;
  336. int sum_ins, sum_dels, sum_lookups ;
  337. int failures = 0, total_failures = 0;
  338. LARGE_INTEGER liFrequency, liT1, liT2;
  339. double duration ;
  340. bool fVerbose = false;
  341. CHAR szIniFile[MAX_PATH];
  342. InitializeIISRTL();
  343. #ifdef _NO_TRACING_
  344. CREATE_DEBUG_PRINT_OBJECT("hashtest");
  345. #endif
  346. printf("\nTest driver for LKRhash"
  347. #ifdef _DEBUG
  348. " (Debug)"
  349. #endif
  350. #ifdef LKR_COMPACT_DELETE
  351. ". (CompactDelete)"
  352. #endif
  353. #ifdef LKR_NEWCODE
  354. ". (NewCode)"
  355. #endif
  356. #ifdef LKR_FIND_FIRST
  357. ". (FindFirst)"
  358. #endif
  359. #ifdef LKR_SUBTABLE
  360. ". (Subtable)"
  361. #endif
  362. #ifdef LKR_MASK
  363. ". (Mask)"
  364. #endif
  365. ".\n\n"
  366. ) ;
  367. #if defined(LKRHASH_ACACHE)
  368. const char szAllocator[] = "ACache";
  369. #elif defined(LKRHASH_MANODEL)
  370. const char szAllocator[] = "MANoDel";
  371. #elif defined(LKRHASH_MADEL)
  372. const char szAllocator[] = "MADel";
  373. #else
  374. const char szAllocator[] = "Default allocator (global operator new)";
  375. #endif
  376. printf("%s version, memory alignment = %d bytes.\n",
  377. szAllocator, LKRHASH_MEM_DEFAULT_ALIGN);
  378. if (argc == 2)
  379. {
  380. GetFullPathName(argv[1], MAX_PATH, szIniFile, NULL);
  381. ifs.ReadIniFile(szIniFile);
  382. }
  383. else
  384. {
  385. fprintf(stderr, "Usage: %s ini-file\n", argv[0]);
  386. exit(1);
  387. }
  388. #ifdef SAMPLE_LKRHASH_TESTCLASS
  389. Test(fVerbose);
  390. if (fVerbose) printf("Test succeeded\n");
  391. #endif // SAMPLE_LKRHASH_TESTCLASS
  392. fp = fopen(ifs.m_szDataFile, "r" ) ;
  393. if (fp == NULL)
  394. {
  395. printf("Can't open file `%s'.\n", ifs.m_szDataFile) ;
  396. exit(1) ;
  397. }
  398. printf("Reading `%s' ", ifs.m_szDataFile);
  399. for (g_nokeys = 0; g_nokeys < ifs.m_nMaxKeys; )
  400. {
  401. if (fgets(sz, sizeof sz, fp) == NULL)
  402. break;
  403. int cch = strlen(sz);
  404. if (cch > 0 && sz[cch-1] == '\n')
  405. sz[--cch] = '\0';
  406. if (cch > 0)
  407. g_wordtable[g_nokeys++].m_str.Set(sz);
  408. if (g_nokeys % 10000 == 0)
  409. putchar('.');
  410. }
  411. fclose(fp) ;
  412. printf("\nLoaded %d keys from `%s', ",g_nokeys, ifs.m_szDataFile ) ;
  413. g_nokeys = expand_key_set(ifs.m_nMaxKeys, g_nokeys, true) ;
  414. printf(" expanded to %d keys.\n\n", g_nokeys) ;
  415. int cchTotal = 0, cchMin = INT_MAX, cchMax = 0;
  416. for (i = 0; i < g_nokeys; ++i)
  417. {
  418. cchTotal += g_wordtable[i].m_str.m_cch;
  419. cchMin = min(cchMin, g_wordtable[i].m_str.m_cch);
  420. cchMax = max(cchMax, g_wordtable[i].m_str.m_cch);
  421. }
  422. srand(ifs.m_nSeed) ;
  423. sprintf(sz, "%d", ifs.m_nInitSize);
  424. if (ifs.m_nInitSize == LK_SMALL_TABLESIZE)
  425. strcpy(sz, "small");
  426. else if (ifs.m_nInitSize == LK_MEDIUM_TABLESIZE)
  427. strcpy(sz, "medium");
  428. else if (ifs.m_nInitSize == LK_LARGE_TABLESIZE)
  429. strcpy(sz, "large");
  430. DWORD initsize2 = ifs.m_nInitSize;
  431. DWORD nsubtbls2 = ifs.m_nSubTables;
  432. LK_TABLESIZE lkts = CWordHash::NumSubTables(ifs.m_nInitSize, nsubtbls2);
  433. printf("Max load = %.1f, initsize = %s, "
  434. "%d subtables (%d tables, size = %d, lkts = %d).\n",
  435. ifs.m_dblHighLoad, sz,
  436. ifs.m_nSubTables, nsubtbls2, initsize2, lkts);
  437. printf("Lookup freq = %d, %d threads, "
  438. "%d round%s.\n",
  439. ifs.m_nLookupFreq, ifs.m_nMaxThreads,
  440. ifs.m_nRounds, (ifs.m_nRounds==1 ? "" : "s"));
  441. printf("%d keys from `%s'. Key length: avg = %d, min = %d, max = %d.\n",
  442. g_nokeys, ifs.m_szDataFile, cchTotal / g_nokeys, cchMin, cchMax);
  443. printf("Base Table = %s. "
  444. "Hash method = %s.\n",
  445. CWordHash::BaseHashTable::ClassName(),
  446. CWordHash::HashMethod());
  447. #ifdef LKRHASH_GLOBAL_LOCK
  448. printf("GlobalLock = %s, "
  449. "%d bytes, "
  450. "Spin Count = %hd, "
  451. "Adj Factor = %.1f.\n",
  452. CWordHash::GlobalLock::ClassName(),
  453. sizeof(CWordHash::GlobalLock),
  454. ifs.m_wTableSpin,
  455. CWordHash::GlobalLock::GetDefaultSpinAdjustmentFactor());
  456. #endif
  457. printf("TableLock = %s, "
  458. "%d bytes, "
  459. "Spin Count = %hd, "
  460. "Adj Factor = %.1f.\n",
  461. CWordHash::TableLock::ClassName(),
  462. sizeof(CWordHash::TableLock),
  463. ifs.m_wTableSpin,
  464. CWordHash::TableLock::GetDefaultSpinAdjustmentFactor());
  465. printf("BucketLock = %s, "
  466. "%d bytes, "
  467. "Spin Count = %hd, "
  468. "Adj Factor = %.1f.\n",
  469. CWordHash::BucketLock::ClassName(),
  470. sizeof(CWordHash::BucketLock),
  471. ifs.m_wBucketSpin,
  472. CWordHash::BucketLock::GetDefaultSpinAdjustmentFactor());
  473. #ifdef LOCK_PER_LOCK_SPINCOUNTS
  474. printf("Per");
  475. #else
  476. printf("No per");
  477. #endif
  478. printf("-lock spincounts. #CPUs = %d. Random seed = %d. "
  479. "Nodes/Clump = %d.\n",
  480. NumProcessors(), ifs.m_nSeed,
  481. #ifdef LKR_NEWCODE
  482. CWordHash::NODES_PER_CLUMP
  483. #else
  484. 6
  485. #endif
  486. );
  487. time_t tmNow;
  488. time(&tmNow);
  489. printf("\nRun: %s\n\n", ctime(&tmNow));
  490. if (ifs.m_fTestIterators)
  491. test_iterators(ifs.m_dblHighLoad, ifs.m_nInitSize, ifs.m_nSubTables);
  492. printf("Starting threads...\n\n");
  493. IRTLVERIFY(QueryPerformanceFrequency(&liFrequency));
  494. printf("QueryPerformanceFrequency = %f\n", (double) liFrequency.QuadPart);
  495. int nTotalOps = 0;
  496. for (num_threads = 1; num_threads <= ifs.m_nMaxThreads; num_threads++ )
  497. {
  498. TRACE("\nStarting %8d\n", num_threads);
  499. IRTLVERIFY(QueryPerformanceCounter(&liT1));
  500. pTbl = new CWordHash(ifs.m_dblHighLoad, ifs.m_nInitSize,
  501. ifs.m_nSubTables) ;
  502. pTbl->SetTableLockSpinCount(ifs.m_wTableSpin);
  503. pTbl->SetBucketLockSpinCount(ifs.m_wBucketSpin);
  504. keys_per_thread = g_nokeys/num_threads ;
  505. for (i = 0; i < num_threads; i++)
  506. {
  507. de_area[i].ptbl = pTbl ;
  508. de_area[i].threadno = i+1 ;
  509. de_area[i].first_key = i*keys_per_thread ;
  510. de_area[i].last_key = ((i == num_threads - 1)
  511. ? g_nokeys
  512. : (i+1)*keys_per_thread) ;
  513. de_area[i].rounds = ifs.m_nRounds ;
  514. de_area[i].highload = (float) ifs.m_dblHighLoad ;
  515. de_area[i].lookup_freq = ifs.m_nLookupFreq ;
  516. de_area[i].hevFinished =
  517. CreateEvent(NULL, // no security attributes
  518. FALSE, // auto reset
  519. FALSE, // not signalled
  520. NULL); // no name
  521. IRTLASSERT(de_area[i].hevFinished != NULL);
  522. ahEvents[i] = de_area[i].hevFinished;
  523. _beginthread(exercise_table, 0, &de_area[i]) ;
  524. }
  525. DWORD dw = WaitForMultipleObjects(num_threads, ahEvents,
  526. TRUE, INFINITE);
  527. for (i = 0; i < num_threads; i++)
  528. CloseHandle(ahEvents[i]);
  529. IRTLVERIFY(QueryPerformanceCounter(&liT2));
  530. duration = (liT2.QuadPart-liT1.QuadPart) /(double)liFrequency.QuadPart;
  531. sum_ins = sum_dels = sum_lookups = 0 ;
  532. for (i = 0; i < num_threads; i++)
  533. {
  534. sum_ins += de_area[i].cinserts ;
  535. sum_dels += de_area[i].cdeletes ;
  536. sum_lookups += de_area[i].clookups ;
  537. failures += de_area[i].cfailures ;
  538. }
  539. int nOps = (int)((sum_ins + sum_dels + sum_lookups) / duration);
  540. total_failures += failures;
  541. nTotalOps += nOps; // TODO: weight?
  542. char szSumIns[16], szSumDels[16], szSumLookups[16], szNOps[16];
  543. #ifndef LOCK_INSTRUMENTATION
  544. if (num_threads == 1)
  545. #endif // LOCK_INSTRUMENTATION
  546. {
  547. printf("%8s %11s %11s "
  548. "%11s %11s %11s\n",
  549. "#Threads", "Ops/sec", "Duration",
  550. "Inserts", "Deletes", "Lookups");
  551. }
  552. char szSummary[200];
  553. sprintf(szSummary, "%8d %11s %11.3f "
  554. "%11s %11s %11s\n",
  555. num_threads,
  556. CommaNumber(nOps, szNOps),
  557. duration,
  558. CommaNumber(sum_ins, szSumIns),
  559. CommaNumber(sum_dels, szSumDels),
  560. CommaNumber(sum_lookups, szSumLookups)
  561. );
  562. printf(szSummary);
  563. TRACE(szSummary);
  564. if (failures != 0)
  565. printf("%d failed operations!\n", failures);
  566. #ifdef LOCK_INSTRUMENTATION
  567. print_lock_statistics(pTbl->GetStatistics());
  568. #ifdef LKRHASH_GLOBAL_LOCK
  569. CWordHash::GlobalLock::ResetGlobalStatistics();
  570. #endif
  571. CWordHash::BucketLock::ResetGlobalStatistics();
  572. CWordHash::TableLock::ResetGlobalStatistics();
  573. printf("\n");
  574. #endif
  575. delete pTbl ;
  576. }
  577. char szNTotalOps[16];
  578. printf("\nAverage Ops = %s.\n",
  579. CommaNumber(nTotalOps / ifs.m_nMaxThreads, szNTotalOps));
  580. if (total_failures != 0)
  581. printf("%d total failed operations!\n", total_failures);
  582. #if defined(MANODEL) && defined(MANODEL_INSTRUMENTATION)
  583. MEMORY_ALLOC_NO_DELETE::PrintStatistics();
  584. #endif
  585. #ifdef _NO_TRACING_
  586. DELETE_DEBUG_PRINT_OBJECT();
  587. #endif
  588. TerminateIISRTL();
  589. LKRHashTableUninit();
  590. #if defined(LKRHASH_ACACHE)
  591. DBG_REQUIRE(ALLOC_CACHE_HANDLER::Cleanup());
  592. #endif
  593. return(0) ;
  594. } /* main */
  595. void test_iterators(
  596. double highload,
  597. int initsize,
  598. int nsubtbls)
  599. {
  600. printf("Testing iterators...\n");
  601. int i;
  602. CWordHash *pTbl = new CWordHash(highload, initsize, nsubtbls) ;
  603. LK_RETCODE lkrc;
  604. IRTLASSERT(0 == pTbl->Size());
  605. IRTLASSERT(pTbl->CheckTable() == 0);
  606. TRACE("Table is empty. Building...\n");
  607. for (i = 0 ; i < g_nokeys ; i++ )
  608. {
  609. lkrc = pTbl->InsertRecord(&g_wordtable[i], false);
  610. if (lkrc != LK_SUCCESS)
  611. TRACE("i = %d, word = `%s', lkrc = %d\n", i, g_wordtable[i], lkrc);
  612. IRTLASSERT(lkrc == LK_SUCCESS);
  613. }
  614. pTbl->ReadLock();
  615. TRACE("Checking that table has %d records (size = %d)\n",
  616. g_nokeys, pTbl->Size());
  617. IRTLASSERT(g_nokeys == (int) pTbl->Size());
  618. IRTLASSERT(pTbl->CheckTable() == 0);
  619. pTbl->ReadUnlock();
  620. TRACE("Clearing the table\n");
  621. pTbl->Clear();
  622. IRTLASSERT(0 == pTbl->Size());
  623. IRTLASSERT(pTbl->CheckTable() == 0);
  624. TRACE("Seeing what crud is left in the table\n");
  625. size_t cRec = 0;
  626. for (i = 0 ; i < g_nokeys ; i++ )
  627. {
  628. CStr* pstrKey = &g_wordtable[i].m_str;
  629. CWord* pkey = NULL;
  630. LK_RETCODE lkrc = pTbl->FindKey(pstrKey, &pkey);
  631. if (pkey != NULL)
  632. {
  633. IRTLASSERT(pkey == &g_wordtable[i]);
  634. --pkey->m_cRefs;
  635. TRACE("%s\n", g_wordtable[i]);
  636. ++cRec;
  637. }
  638. }
  639. TRACE("Found %d records that shouldn't have been there\n", cRec);
  640. TRACE("Rebuilding the table\n");
  641. for (i = 0 ; i < g_nokeys ; i++ )
  642. IRTLVERIFY(pTbl->InsertRecord(&g_wordtable[i]) == LK_SUCCESS);
  643. IRTLASSERT(g_nokeys == (int) pTbl->Size());
  644. IRTLASSERT(pTbl->CheckTable() == 0);
  645. TRACE("Checking iterators\n");
  646. cRec = 0;
  647. CWordHash::CIterator iter(LKL_READLOCK);
  648. for (lkrc = pTbl->InitializeIterator(&iter);
  649. lkrc == LK_SUCCESS;
  650. lkrc = pTbl->IncrementIterator(&iter))
  651. {
  652. ++cRec;
  653. const CStr* pstrKey = iter.Key();
  654. CWord* pRec = iter.Record();
  655. IRTLASSERT(&g_wordtable[0] <= pRec && pRec < &g_wordtable[g_nokeys]);
  656. IRTLASSERT(!pRec->m_fIterated);
  657. pRec->m_fIterated = true;
  658. #ifdef LKR_NEWCODE
  659. if (CWordHash::TableLock::Recursion() != LOCK_NON_RECURSIVE
  660. && CWordHash::BucketLock::Recursion() != LOCK_NON_RECURSIVE)
  661. #endif
  662. {
  663. // Check that the lock can be safely acquired recursively
  664. // (the table is already locked by the iterator).
  665. int x = rand() % g_nokeys;
  666. CStr* pstrKey2 = &g_wordtable[x].m_str;
  667. CWord* pkey2 = NULL;
  668. LK_RETCODE lkrc2= pTbl->FindKey(pstrKey2, &pkey2);
  669. IRTLASSERT(lkrc2 == LK_SUCCESS && pkey2 == &g_wordtable[x]);
  670. if (pkey2 != NULL)
  671. --pkey2->m_cRefs;
  672. }
  673. }
  674. IRTLASSERT(lkrc == LK_NO_MORE_ELEMENTS);
  675. IRTLASSERT((int) cRec == g_nokeys);
  676. lkrc = pTbl->CloseIterator(&iter);
  677. IRTLASSERT(lkrc == LK_SUCCESS);
  678. for (i = 0 ; i < g_nokeys ; i++ )
  679. {
  680. IRTLASSERT(g_wordtable[i].m_fIterated);
  681. g_wordtable[i].m_fIterated = false;
  682. }
  683. do {
  684. cRec = rand() % (g_nokeys - 1);
  685. } while (cRec == 0);
  686. TRACE("Checking abandoning of const iterators after %d iterations\n",
  687. cRec);
  688. const CWordHash *pTblConst = pTbl;
  689. CWordHash::CConstIterator iterConst;
  690. for (lkrc = pTblConst->InitializeIterator(&iterConst);
  691. lkrc == LK_SUCCESS;
  692. lkrc = pTblConst->IncrementIterator(&iterConst))
  693. {
  694. if (--cRec == 0)
  695. break;
  696. const CStr* pszKey = iterConst.Key();
  697. const CWord* pRec = iterConst.Record();
  698. IRTLASSERT(&g_wordtable[0] <= pRec && pRec < &g_wordtable[g_nokeys]);
  699. }
  700. IRTLASSERT(lkrc != LK_NO_MORE_ELEMENTS);
  701. lkrc = pTblConst->CloseIterator(&iterConst);
  702. IRTLASSERT(lkrc == LK_SUCCESS);
  703. TRACE("Gathering statistics\n");
  704. CLKRHashTableStats stats = pTbl->GetStatistics();
  705. print_table_statistics(stats);
  706. #ifdef LOCK_INSTRUMENTATION
  707. print_lock_statistics(stats);
  708. CWordHash::BucketLock::ResetGlobalStatistics();
  709. CWordHash::TableLock::ResetGlobalStatistics();
  710. #endif
  711. printf("\n");
  712. TRACE("Cleaning up by hand\n");
  713. for (i = 0 ; i < g_nokeys ; i++ )
  714. {
  715. IRTLVERIFY(pTbl->DeleteKey(&g_wordtable[i].m_str) == LK_SUCCESS);
  716. }
  717. IRTLASSERT(0 == pTbl->Size());
  718. delete pTbl ;
  719. }
  720. void print_table_statistics(const CLKRHashTableStats& stats)
  721. {
  722. printf("#Records=%d, #BucketChains=%d, "
  723. "DirSize=%d, LongestChain=%3d,\n",
  724. stats.RecordCount, stats.TableSize,
  725. stats.DirectorySize, stats.LongestChain);
  726. printf("#Empty Buckets=%d, Split Factor=%.2f, "
  727. "AvgSrchLen=%.2f, Expected SL=%.2f,\n",
  728. stats.EmptySlots, stats.SplitFactor,
  729. stats.AvgSearchLength, stats.ExpSearchLength);
  730. printf("Avg Unsuccessful SrchLen=%.2f, ExpUSL=%.2f.\n",
  731. stats.AvgUSearchLength, stats.ExpUSearchLength);
  732. printf("\nBucket Chain Lengths "
  733. "(node clump size = %d, bucket size = %d bytes):\n",
  734. stats.NodeClumpSize, stats.CBucketSize);
  735. for (int j = 0; j < CLKRHashTableStats::MAX_BUCKETS; ++j)
  736. {
  737. if (stats.m_aBucketLenHistogram[j] == 0)
  738. {
  739. printf("\n");
  740. break;
  741. }
  742. printf(" %10d: %6d",
  743. stats.BucketSize(j), stats.m_aBucketLenHistogram[j]);
  744. if (j % 4 == 3)
  745. printf("\n");
  746. }
  747. printf("\n");
  748. }
  749. #ifdef LOCK_INSTRUMENTATION
  750. void print_lock_statistics(const CLKRHashTableStats& stats)
  751. {
  752. printf("Global Locks Statistics:"
  753. "\n total locks created = %ld, "
  754. "total contentions = %ld, "
  755. "#sleeps = %ld,"
  756. "\n total spins = %I64d, "
  757. "av spins/contention = %.1f, "
  758. "\n #readlocks = %d, "
  759. "#writelocks=%d\n",
  760. stats.m_gls.m_cTotalLocks,
  761. stats.m_gls.m_cContendedLocks,
  762. stats.m_gls.m_nSleeps,
  763. stats.m_gls.m_cTotalSpins,
  764. stats.m_gls.m_nAverageSpins,
  765. stats.m_gls.m_nReadLocks,
  766. stats.m_gls.m_nWriteLocks
  767. );
  768. printf("Averaged SubTable Locks Statistics:"
  769. "\n Total locks = %d, "
  770. "#contentions = %.1f, "
  771. "sleeps = %.1f; "
  772. "\n total spins = %.1f, "
  773. "avg spins = %.1f, "
  774. "\n #readlocks = %.1f, "
  775. "#writelocks=%.1f\n",
  776. stats.m_alsTable.m_nItems,
  777. stats.m_alsTable.m_nContentions,
  778. stats.m_alsTable.m_nSleeps,
  779. stats.m_alsTable.m_nContentionSpins,
  780. stats.m_alsTable.m_nAverageSpins,
  781. stats.m_alsTable.m_nReadLocks,
  782. stats.m_alsTable.m_nWriteLocks);
  783. printf("Averaged Bucket Locks Statistics:"
  784. "\n Total locks = %d, "
  785. "#contentions = %.1f, "
  786. "sleeps = %.1f; "
  787. "\n total spins = %.1f, "
  788. "avg spins = %.1f, "
  789. "\n #readlocks = %.1f, "
  790. "#writelocks=%.1f\n",
  791. stats.m_alsBucketsAvg.m_nItems,
  792. stats.m_alsBucketsAvg.m_nContentions,
  793. stats.m_alsBucketsAvg.m_nSleeps,
  794. stats.m_alsBucketsAvg.m_nContentionSpins,
  795. stats.m_alsBucketsAvg.m_nAverageSpins,
  796. stats.m_alsBucketsAvg.m_nReadLocks,
  797. stats.m_alsBucketsAvg.m_nWriteLocks);
  798. printf("\n");
  799. }
  800. #endif // LOCK_INSTRUMENTATION
  801. int expand_key_set(int maxkeys, int numkeys, bool fVerbose)
  802. {
  803. int totkeys = numkeys ;
  804. if (totkeys > maxkeys)
  805. return maxkeys;
  806. char* pszTemp = (char*) _alloca(20 + CStr::sm_cchMax);
  807. for(int k = 0; TRUE; k++)
  808. {
  809. for(int i = 0; i < numkeys; i++)
  810. {
  811. if (totkeys == maxkeys)
  812. return(totkeys) ;
  813. sprintf(pszTemp, "%d%s", k, g_wordtable[i].m_str.m_psz);
  814. g_wordtable[totkeys++].m_str.Set(pszTemp);
  815. }
  816. if (fVerbose) putchar('.');
  817. }
  818. }
  819. void __cdecl exercise_table(void* pinput)
  820. {
  821. CWordHash* pTbl;
  822. thread_data* pdea = (thread_data*) pinput ;
  823. int cfailed_ins=0 ;
  824. int cfailed_dels=0 ;
  825. int cFoundSuccesses=0, cFoundFails=0 ;
  826. int x, i ;
  827. LARGE_INTEGER liFreq, liT1, liT2;
  828. SetThreadIdealProcessor(GetCurrentThread(),
  829. pdea->threadno % NumProcessors());
  830. IRTLVERIFY(QueryPerformanceFrequency(&liFreq));
  831. IRTLVERIFY(QueryPerformanceCounter(&liT1));
  832. pdea->cinserts = 0 ;
  833. pdea->cdeletes = 0 ;
  834. pdea->clookups = 0 ;
  835. pTbl = pdea->ptbl ;
  836. for (int rnd = 0; rnd < pdea->rounds; rnd++)
  837. {
  838. IRTLASSERT(pTbl->CheckTable() == 0);
  839. // Insert all the keys, randomly searching after each insertion
  840. for (i = pdea->first_key ; i < pdea->last_key ; i++ )
  841. {
  842. #ifdef _DEBUG
  843. CStr* pstrKey = &g_wordtable[i].m_str;
  844. CWord* pkey = NULL;
  845. LK_RETCODE lkrc = pTbl->FindKey(pstrKey, &pkey);
  846. IRTLASSERT(lkrc == LK_NO_SUCH_KEY && pkey == NULL);
  847. #endif // _DEBUG
  848. if (pTbl->InsertRecord(&g_wordtable[i] ) != LK_SUCCESS )
  849. {
  850. cfailed_ins++ ;
  851. }
  852. else
  853. {
  854. #ifdef _DEBUG
  855. pstrKey = &g_wordtable[i].m_str;
  856. lkrc = pTbl->FindKey(pstrKey, &pkey);
  857. IRTLASSERT(lkrc == LK_SUCCESS && pkey == &g_wordtable[i]);
  858. --pkey->m_cRefs;
  859. #endif // _DEBUG
  860. g_wordtable[i].m_fInserted = true;
  861. }
  862. pdea->cinserts++ ;
  863. for (int lu = 0; lu < pdea->lookup_freq; lu++)
  864. {
  865. x = rand() % (pdea->last_key - pdea->first_key)
  866. + pdea->first_key;
  867. bool fPresent = (x <= i); // should it be found?
  868. CWord* pkey = NULL;
  869. LK_RETCODE lkrc = pTbl->FindKey(&g_wordtable[x].m_str, &pkey);
  870. if (fPresent)
  871. {
  872. if (lkrc != LK_SUCCESS || pkey != &g_wordtable[x] )
  873. {
  874. ++g_wordtable[x].m_cNotFound;
  875. TRACE("%d: Not found (%s): x = %d, i = %d, "
  876. "cnf = %d, rnd = %d, lkrc = %d, pkey(%s), %d\n",
  877. pdea->threadno, g_wordtable[x].m_str.m_psz, x, i,
  878. g_wordtable[x].m_cNotFound, rnd, lkrc,
  879. pkey != NULL ? pkey->m_str.m_psz : "<null>",
  880. pkey != NULL ? (pkey - g_wordtable) / sizeof(CWord) : -1);
  881. cFoundFails++ ;
  882. }
  883. else
  884. {
  885. --g_wordtable[x].m_cRefs;
  886. cFoundSuccesses++ ;
  887. }
  888. }
  889. else // not fPresent
  890. {
  891. IRTLASSERT(lkrc != LK_SUCCESS && pkey == NULL);
  892. if (lkrc == LK_SUCCESS || pkey != NULL)
  893. {
  894. TRACE("%d: found when not present (%s): "
  895. "x = %d, i = %d, "
  896. "cnf = %d, rnd = %d, lkrc = %d, pkey(%s), %d\n",
  897. pdea->threadno, g_wordtable[x].m_str.m_psz,
  898. x, i,
  899. g_wordtable[x].m_cNotFound, rnd, lkrc,
  900. pkey != NULL ? pkey->m_str.m_psz : "<null>",
  901. pkey != NULL ? (pkey - g_wordtable) / sizeof(CWord) : -1);
  902. cFoundFails++ ;
  903. }
  904. else
  905. {
  906. // wasn't found, but it wasn't present, so this is good
  907. cFoundSuccesses++ ;
  908. }
  909. }
  910. }
  911. pdea->clookups += pdea->lookup_freq ;
  912. }
  913. IRTLASSERT(cfailed_ins == 0) ;
  914. IRTLASSERT(cFoundFails == 0) ;
  915. IRTLASSERT(cFoundSuccesses == ((2 * rnd + 1) * pdea->lookup_freq
  916. * (pdea->last_key - pdea->first_key)));
  917. TRACE("Thrd %u, rnd %d: %d inserts done, not found %d, "
  918. "f=%d, l=%d\n",
  919. pdea->threadno, rnd, pdea->cinserts, cFoundFails,
  920. pdea->first_key, pdea->last_key) ;
  921. IRTLASSERT(pTbl->CheckTable() == 0);
  922. // Delete all the keys, randomly searching before each deletion
  923. for (i = pdea->first_key ; i < pdea->last_key ; i++ )
  924. {
  925. for (int lu = 0; lu < pdea->lookup_freq; lu++)
  926. {
  927. x = rand() % (pdea->last_key - pdea->first_key)
  928. + pdea->first_key;
  929. bool fPresent = (x >= i); // should it be found?
  930. CWord* pkey = NULL;
  931. LK_RETCODE lkrc = pTbl->FindKey(&g_wordtable[x].m_str, &pkey);
  932. if (fPresent)
  933. {
  934. if (lkrc != LK_SUCCESS || pkey != &g_wordtable[x] )
  935. {
  936. ++g_wordtable[x].m_cNotFound;
  937. TRACE("%d: Not found (%s): x = %d, i = %d, "
  938. "cnf = %d, rnd = %d, lkrc = %d, pkey(%s), %d\n",
  939. pdea->threadno, g_wordtable[x].m_str.m_psz, x, i,
  940. g_wordtable[x].m_cNotFound, rnd, lkrc,
  941. pkey != NULL ? pkey->m_str.m_psz : "<null>",
  942. pkey != NULL ? (pkey - g_wordtable) / sizeof(CWord) : -1);
  943. cFoundFails++ ;
  944. }
  945. else
  946. {
  947. --g_wordtable[x].m_cRefs;
  948. cFoundSuccesses++ ;
  949. }
  950. }
  951. else // !fPresent
  952. {
  953. IRTLASSERT(lkrc != LK_SUCCESS && pkey == NULL);
  954. if (lkrc == LK_SUCCESS || pkey != NULL)
  955. {
  956. TRACE("%d: found when not present (%s): "
  957. "x = %d, i = %d, "
  958. "cnf = %d, rnd = %d, lkrc = %d, pkey(%s), %d\n",
  959. pdea->threadno, g_wordtable[x].m_str.m_psz,
  960. x, i,
  961. g_wordtable[x].m_cNotFound, rnd, lkrc,
  962. pkey != NULL ? pkey->m_str.m_psz : "<null>",
  963. pkey != NULL ? (pkey - g_wordtable) / sizeof(CWord) : -1);
  964. cFoundFails++ ;
  965. }
  966. else
  967. {
  968. // wasn't found, but it wasn't present, so this is good
  969. cFoundSuccesses++ ;
  970. }
  971. }
  972. }
  973. pdea->clookups += pdea->lookup_freq ;
  974. #ifdef _DEBUG
  975. CStr* pstrKey = &g_wordtable[i].m_str;
  976. CWord* pkey = NULL;
  977. LK_RETCODE lkrc = pTbl->FindKey(pstrKey, &pkey);
  978. IRTLASSERT(lkrc == LK_SUCCESS && pkey == &g_wordtable[i]);
  979. --pkey->m_cRefs;
  980. #endif // _DEBUG
  981. if (pTbl->DeleteKey(&g_wordtable[i].m_str) != LK_SUCCESS )
  982. {
  983. cfailed_dels++ ;
  984. }
  985. else
  986. {
  987. #ifdef _DEBUG
  988. pstrKey = &g_wordtable[i].m_str;
  989. lkrc = pTbl->FindKey(pstrKey, &pkey);
  990. IRTLASSERT(lkrc == LK_NO_SUCH_KEY && pkey == NULL);
  991. #endif // _DEBUG
  992. g_wordtable[i].m_fInserted = false;
  993. }
  994. pdea->cdeletes++ ;
  995. }
  996. #ifdef _DEBUG
  997. int cBadKeys = 0;
  998. for (i = pdea->first_key ; i < pdea->last_key ; i++ )
  999. {
  1000. if (g_wordtable[i].m_cNotFound > 0)
  1001. {
  1002. ++cBadKeys;
  1003. TRACE("%-20s: #not found = %d, hash = %d, %08x\n",
  1004. g_wordtable[i].m_str.m_psz,
  1005. g_wordtable[i].m_cNotFound,
  1006. CWordHash::CalcKeyHash(CWordHash::ExtractKey(
  1007. &g_wordtable[i])),
  1008. CWordHash::CalcKeyHash(CWordHash::ExtractKey(
  1009. &g_wordtable[i])));
  1010. }
  1011. }
  1012. if (cBadKeys > 0)
  1013. TRACE("%d bad keys\n", cBadKeys);
  1014. IRTLASSERT(cBadKeys == 0);
  1015. #endif // _DEBUG
  1016. IRTLASSERT(cfailed_dels == 0 ) ;
  1017. IRTLASSERT(cFoundFails == 0 ) ;
  1018. IRTLASSERT(cFoundSuccesses == ((2 * rnd + 2) * pdea->lookup_freq
  1019. * (pdea->last_key - pdea->first_key)));
  1020. TRACE("Thrd %u, rnd %d: %d deletes done, not found %d, "
  1021. "f=%d, l=%d\n",
  1022. pdea->threadno, rnd, pdea->cdeletes, cFoundFails,
  1023. pdea->first_key, pdea->last_key) ;
  1024. } // (for rnd)
  1025. IRTLVERIFY(QueryPerformanceCounter(&liT2));
  1026. pdea->duration = (liT2.QuadPart-liT1.QuadPart) / (double) liFreq.QuadPart;
  1027. IRTLASSERT(pTbl->CheckTable() == 0);
  1028. TRACE("Thread %u terminating: %d found, %d not found\n",
  1029. pdea->threadno, cFoundSuccesses, cFoundFails) ;
  1030. if (cFoundSuccesses != (2 * pdea->rounds * pdea->lookup_freq
  1031. * (pdea->last_key - pdea->first_key))
  1032. || cFoundFails != 0 || cfailed_ins != 0 || cfailed_dels != 0)
  1033. {
  1034. printf("Thread %u: found = %d, not found = %d, "
  1035. "\nfailed inserts = %d, failed deletes = %d\n",
  1036. pdea->threadno, cFoundSuccesses, cFoundFails,
  1037. cfailed_ins, cfailed_dels);
  1038. }
  1039. pdea->cfailures = cfailed_ins + cfailed_dels + cFoundFails;
  1040. if (pdea->hevFinished != NULL)
  1041. SetEvent(pdea->hevFinished);
  1042. }