Changeset 76873 in vbox
- Timestamp:
- Jan 18, 2019 5:07:58 AM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/utils/fs/FsPerf.cpp
r76801 r76873 1 1 /* $Id$ */ 2 2 /** @file 3 * NetPerf - File SystemPerformance Benchmark.3 * FsPerf - File System (Shared Folders) Performance Benchmark. 4 4 */ 5 5 … … 48 48 #include <iprt/time.h> 49 49 #include <iprt/thread.h> 50 #include <iprt/zero.h> 50 51 51 52 … … 89 90 #define PROFILE_MANYTREE_FN(a_szPath, a_fnCall, a_cEstimationIterations, a_cNsTarget, a_szDesc) \ 90 91 do { \ 92 if (!g_fManyFiles) \ 93 break; \ 94 \ 91 95 /* Estimate how many iterations we need to fill up the given timeslot: */ \ 92 96 uint64_t const nsStartEstimation = RTTimeNanoTS(); \ … … 158 162 159 163 164 enum 165 { 166 kCmdOpt_First = 128, 167 168 kCmdOpt_ManyFiles = kCmdOpt_First, 169 kCmdOpt_NoManyFiles, 170 kCmdOpt_Open, 171 kCmdOpt_NoOpen, 172 kCmdOpt_FStat, 173 kCmdOpt_NoFStat, 174 kCmdOpt_FChMod, 175 kCmdOpt_NoFChMod, 176 kCmdOpt_FUtimes, 177 kCmdOpt_NoFUtimes, 178 kCmdOpt_Stat, 179 kCmdOpt_NoStat, 180 kCmdOpt_ChMod, 181 kCmdOpt_NoChMod, 182 kCmdOpt_Utimes, 183 kCmdOpt_NoUtimes, 184 kCmdOpt_Rename, 185 kCmdOpt_NoRename, 186 kCmdOpt_DirEnum, 187 kCmdOpt_NoDirEnum, 188 kCmdOpt_MkRmDir, 189 kCmdOpt_NoMkRmDir, 190 kCmdOpt_StatVfs, 191 kCmdOpt_NoStatVfs, 192 kCmdOpt_Rm, 193 kCmdOpt_NoRm, 194 kCmdOpt_ChSize, 195 kCmdOpt_NoChSize, 196 kCmdOpt_Read, 197 kCmdOpt_NoRead, 198 kCmdOpt_Write, 199 kCmdOpt_NoWrite, 200 kCmdOpt_Seek, 201 kCmdOpt_NoSeek, 202 203 kCmdOpt_End 204 }; 205 160 206 /********************************************************************************************************************************* 161 207 * Global Variables * … … 166 212 { "--dir", 'd', RTGETOPT_REQ_STRING }, 167 213 { "--seconds", 's', RTGETOPT_REQ_UINT32 }, 214 { "--milliseconds", 'm', RTGETOPT_REQ_UINT64 }, 215 216 { "--enable-all", 'e', RTGETOPT_REQ_NOTHING }, 217 { "--disable-all", 'z', RTGETOPT_REQ_NOTHING }, 218 219 { "--many-files", kCmdOpt_ManyFiles, RTGETOPT_REQ_NOTHING }, 220 { "--no-many-files", kCmdOpt_NoManyFiles, RTGETOPT_REQ_NOTHING }, 221 222 { "--open", kCmdOpt_Open, RTGETOPT_REQ_NOTHING }, 223 { "--no-open", kCmdOpt_NoOpen, RTGETOPT_REQ_NOTHING }, 224 { "--fstat", kCmdOpt_FStat, RTGETOPT_REQ_NOTHING }, 225 { "--no-fstat", kCmdOpt_NoFStat, RTGETOPT_REQ_NOTHING }, 226 { "--fchmod", kCmdOpt_FChMod, RTGETOPT_REQ_NOTHING }, 227 { "--no-fchmod", kCmdOpt_NoFChMod, RTGETOPT_REQ_NOTHING }, 228 { "--futimes", kCmdOpt_FUtimes, RTGETOPT_REQ_NOTHING }, 229 { "--no-futimes", kCmdOpt_NoFUtimes, RTGETOPT_REQ_NOTHING }, 230 { "--stat", kCmdOpt_Stat, RTGETOPT_REQ_NOTHING }, 231 { "--no-stat", kCmdOpt_NoStat, RTGETOPT_REQ_NOTHING }, 232 { "--chmod", kCmdOpt_ChMod, RTGETOPT_REQ_NOTHING }, 233 { "--no-chmod", kCmdOpt_NoChMod, RTGETOPT_REQ_NOTHING }, 234 { "--utimes", kCmdOpt_Utimes, RTGETOPT_REQ_NOTHING }, 235 { "--no-utimes", kCmdOpt_NoUtimes, RTGETOPT_REQ_NOTHING }, 236 { "--rename", kCmdOpt_Rename, RTGETOPT_REQ_NOTHING }, 237 { "--no-rename", kCmdOpt_NoRename, RTGETOPT_REQ_NOTHING }, 238 { "--dir-enum", kCmdOpt_DirEnum, RTGETOPT_REQ_NOTHING }, 239 { "--no-dir-enum", kCmdOpt_NoDirEnum, RTGETOPT_REQ_NOTHING }, 240 { "--mk-rm-dir", kCmdOpt_MkRmDir, RTGETOPT_REQ_NOTHING }, 241 { "--no-mk-rm-dir", kCmdOpt_NoMkRmDir, RTGETOPT_REQ_NOTHING }, 242 { "--stat-vfs", kCmdOpt_StatVfs, RTGETOPT_REQ_NOTHING }, 243 { "--no-stat-vfs", kCmdOpt_NoStatVfs, RTGETOPT_REQ_NOTHING }, 244 { "--rm", kCmdOpt_Rm, RTGETOPT_REQ_NOTHING }, 245 { "--no-rm", kCmdOpt_NoRm, RTGETOPT_REQ_NOTHING }, 246 { "--chsize", kCmdOpt_ChSize, RTGETOPT_REQ_NOTHING }, 247 { "--no-chsize", kCmdOpt_NoChSize, RTGETOPT_REQ_NOTHING }, 248 { "--read", kCmdOpt_Read, RTGETOPT_REQ_NOTHING }, 249 { "--no-read", kCmdOpt_NoRead, RTGETOPT_REQ_NOTHING }, 250 { "--write", kCmdOpt_Write, RTGETOPT_REQ_NOTHING }, 251 { "--no-write", kCmdOpt_NoWrite, RTGETOPT_REQ_NOTHING }, 252 { "--seek", kCmdOpt_Seek, RTGETOPT_REQ_NOTHING }, 253 { "--no-seek", kCmdOpt_NoSeek, RTGETOPT_REQ_NOTHING }, 254 255 { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, 168 256 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, 169 257 { "--version", 'V', RTGETOPT_REQ_NOTHING }, … … 173 261 /** The test handle. */ 174 262 static RTTEST g_hTest; 263 /** The number of nanoseconds a RTTimeNanoTS call takes. 264 * This is used for adjusting loop count estimates. */ 265 static uint64_t g_nsPerNanoTSCall = 1; 175 266 /** Verbosity level. */ 176 267 static uint32_t g_uVerbosity = 0; … … 179 270 * @{ */ 180 271 static bool g_fManyFiles = true; 272 static bool g_fOpen = true; 273 static bool g_fFStat = true; 274 static bool g_fFChMod = true; 275 static bool g_fFUtimes = true; 276 static bool g_fStat = true; 277 static bool g_fChMod = true; 278 static bool g_fUtimes = true; 279 static bool g_fRename = true; 280 static bool g_fDirEnum = true; 281 static bool g_fMkRmDir = true; 282 static bool g_fStatVfs = true; 283 static bool g_fRm = true; 284 static bool g_fChSize = true; 285 static bool g_fSeek = true; 286 static bool g_fRead = true; 287 static bool g_fWrite = true; 181 288 /** @} */ 182 289 … … 188 295 189 296 /** Number of files in the 'manytree' directory tree. */ 190 static uint32_t g_cManyTreeFiles = 640 + 16*640 /*10880*/;297 static uint32_t g_cManyTreeFiles = 640 + 16*640 /*10880*/; 191 298 /** Number of files per directory in the 'manytree' construct. */ 192 static uint32_t g_cManyTreeFilesPerDir = 640;299 static uint32_t g_cManyTreeFilesPerDir = 640; 193 300 /* Number of subdirs per directory in the 'manytree' construct. */ 194 301 static uint32_t g_cManyTreeSubdirsPerDir = 16; 195 302 /** The depth of the 'manytree' directory tree. */ 196 static uint32_t g_cManyTreeDepth = 1;303 static uint32_t g_cManyTreeDepth = 1; 197 304 /** List of directories in the many tree, creation order. */ 198 305 static RTLISTANCHOR g_ManyTreeHead; 306 307 /** Number of configured I/O block sizes. */ 308 static uint32_t g_cIoBlocks = 8; 309 /** Configured I/O block sizes. */ 310 static uint32_t g_acbIoBlocks[16] = { 1, 512, 4096, 16384, 65536, _1M, _32M, _128M }; 311 /** The desired size of the test file we use for I/O. */ 312 static uint64_t g_cbIoFile = _512M; 199 313 200 314 /** The length of g_szDir. */ … … 220 334 RTThreadYield(); 221 335 RTThreadYield(); 336 } 337 338 339 /** 340 * Profiles the RTTimeNanoTS call, setting g_nsPerNanoTSCall. 341 */ 342 static void fsPerfNanoTS(void) 343 { 344 fsPerfYield(); 345 346 /* Make sure we start off on a changing timestamp on platforms will low time resoultion. */ 347 uint64_t nsStart = RTTimeNanoTS(); 348 uint64_t ns; 349 do 350 ns = RTTimeNanoTS(); 351 while (ns == nsStart); 352 nsStart = ns; 353 354 /* Call it for 10 ms. */ 355 uint32_t i = 0; 356 do 357 { 358 i++; 359 ns = RTTimeNanoTS(); 360 } 361 while (ns - nsStart < RT_NS_10MS); 362 363 g_nsPerNanoTSCall = (ns - nsStart) / i; 222 364 } 223 365 … … 753 895 * The directory with many files in it. 754 896 */ 755 fDots = 0; 756 uint32_t const cBitmap = RT_ALIGN_32(g_cManyFiles, 64); 757 void *pvBitmap = alloca(cBitmap / 8); 758 RT_BZERO(pvBitmap, cBitmap / 8); 759 for (uint32_t i = g_cManyFiles; i < cBitmap; i++) 760 ASMBitSet(pvBitmap, i); 761 762 uint32_t cFiles = 0; 763 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS); 764 for (;;) 765 { 766 int rc = RTDirRead(hDir, &Entry, NULL); 767 if (rc == VINF_SUCCESS) 897 if (g_fManyFiles) 898 { 899 fDots = 0; 900 uint32_t const cBitmap = RT_ALIGN_32(g_cManyFiles, 64); 901 void *pvBitmap = alloca(cBitmap / 8); 902 RT_BZERO(pvBitmap, cBitmap / 8); 903 for (uint32_t i = g_cManyFiles; i < cBitmap; i++) 904 ASMBitSet(pvBitmap, i); 905 906 uint32_t cFiles = 0; 907 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS); 908 for (;;) 768 909 { 769 if (Entry.szName[0] == '.') 910 int rc = RTDirRead(hDir, &Entry, NULL); 911 if (rc == VINF_SUCCESS) 770 912 { 771 if (Entry.szName[ 1] == '.')913 if (Entry.szName[0] == '.') 772 914 { 773 RTTESTI_CHECK(!(fDots & 2)); 774 fDots |= 2; 915 if (Entry.szName[1] == '.') 916 { 917 RTTESTI_CHECK(!(fDots & 2)); 918 fDots |= 2; 919 } 920 else 921 { 922 RTTESTI_CHECK(Entry.szName[1] == '\0'); 923 RTTESTI_CHECK(!(fDots & 1)); 924 fDots |= 1; 925 } 775 926 } 776 927 else 777 928 { 778 RTTESTI_CHECK(Entry.szName[1] == '\0'); 779 RTTESTI_CHECK(!(fDots & 1)); 780 fDots |= 1; 929 uint32_t iFile = UINT32_MAX; 930 RTTESTI_CHECK_RC(RTStrToUInt32Full(Entry.szName, 10, &iFile), VINF_SUCCESS); 931 if ( iFile < g_cManyFiles 932 && !ASMBitTest(pvBitmap, iFile)) 933 { 934 ASMBitSet(pvBitmap, iFile); 935 cFiles++; 936 } 937 else 938 RTTestFailed(g_hTest, "line %u: iFile=%u g_cManyFiles=%u\n", __LINE__, iFile, g_cManyFiles); 781 939 } 782 940 } 941 else if (rc == VERR_NO_MORE_FILES) 942 break; 783 943 else 784 944 { 785 uint32_t iFile = UINT32_MAX; 786 RTTESTI_CHECK_RC(RTStrToUInt32Full(Entry.szName, 10, &iFile), VINF_SUCCESS); 787 if ( iFile < g_cManyFiles 788 && !ASMBitTest(pvBitmap, iFile)) 789 { 790 ASMBitSet(pvBitmap, iFile); 791 cFiles++; 792 } 793 else 794 RTTestFailed(g_hTest, "line %u: iFile=%u g_cManyFiles=%u\n", __LINE__, iFile, g_cManyFiles); 945 RTTestFailed(g_hTest, "RTDirRead failed enumerating manyfiles: %Rrc\n", rc); 946 RTDirClose(hDir); 947 return; 795 948 } 796 949 } 797 else if (rc == VERR_NO_MORE_FILES) 798 break; 799 else 800 { 801 RTTestFailed(g_hTest, "RTDirRead failed enumerating manyfiles: %Rrc\n", rc); 802 RTDirClose(hDir); 803 return; 804 } 805 } 806 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS); 807 RTTESTI_CHECK(fDots == 3); 808 RTTESTI_CHECK(cFiles == g_cManyFiles); 809 RTTESTI_CHECK(ASMMemIsAllU8(pvBitmap, cBitmap / 8, 0xff)); 950 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS); 951 RTTESTI_CHECK(fDots == 3); 952 RTTESTI_CHECK(cFiles == g_cManyFiles); 953 RTTESTI_CHECK(ASMMemIsAllU8(pvBitmap, cBitmap / 8, 0xff)); 954 } 810 955 811 956 /* 812 957 * Profile. 813 958 */ 814 PROFILE_FN(fsPerfEnumEmpty(), g_nsTestRun, "RTDirOpen/Read/Close empty"); 815 PROFILE_FN(fsPerfEnumManyFiles(), g_nsTestRun, "RTDirOpen/Read/Close manyfiles"); 816 } 817 818 819 void fsPerfMkDirRmDir(void) 959 PROFILE_FN(fsPerfEnumEmpty(),g_nsTestRun, "RTDirOpen/Read/Close empty"); 960 if (g_fManyFiles) 961 PROFILE_FN(fsPerfEnumManyFiles(), g_nsTestRun, "RTDirOpen/Read/Close manyfiles"); 962 } 963 964 965 void fsPerfMkRmDir(void) 820 966 { 821 967 RTTestISub("mkdir/rmdir"); … … 850 996 * Profile alternately creating and removing a bunch of directories. 851 997 */ 998 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("subdir-2")), 0755, 0), VINF_SUCCESS); 999 size_t cchDir = strlen(g_szDir); 1000 g_szDir[cchDir++] = RTPATH_SLASH; 1001 g_szDir[cchDir++] = 's'; 1002 1003 uint32_t cCreated = 0; 1004 uint64_t nsCreate = 0; 1005 uint64_t nsRemove = 0; 852 1006 for (;;) 853 1007 { 854 1008 /* Create a bunch: */ 1009 uint64_t nsStart = RTTimeNanoTS(); 1010 for (uint32_t i = 0; i < 998; i++) 1011 { 1012 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD); 1013 RTTESTI_CHECK_RC_RETV(RTDirCreate(g_szDir, 0755, 0), VINF_SUCCESS); 1014 } 1015 nsCreate += RTTimeNanoTS() - nsStart; 1016 cCreated += 998; 855 1017 856 1018 /* Remove the bunch: */ 1019 nsStart = RTTimeNanoTS(); 1020 for (uint32_t i = 0; i < 998; i++) 1021 { 1022 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD); 1023 RTTESTI_CHECK_RC_RETV(RTDirRemove(g_szDir), VINF_SUCCESS); 1024 } 1025 nsRemove = RTTimeNanoTS() - nsStart; 857 1026 858 1027 /* Check if we got time for another round: */ 859 break; 860 } 1028 if ( ( nsRemove >= g_nsTestRun 1029 && nsCreate >= g_nsTestRun) 1030 || nsCreate + nsRemove >= g_nsTestRun * 3) 1031 break; 1032 } 1033 RTTestIValue("RTDirCreate", nsCreate / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE); 1034 RTTestIValue("RTDirRemove", nsRemove / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE); 1035 } 1036 1037 1038 void fsPerfStatVfs(void) 1039 { 1040 RTTestISub("statvfs"); 1041 1042 g_szEmptyDir[g_cchEmptyDir] = '\0'; 1043 RTFOFF cbTotal; 1044 RTFOFF cbFree; 1045 uint32_t cbBlock; 1046 uint32_t cbSector; 1047 RTTESTI_CHECK_RC(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), VINF_SUCCESS); 1048 1049 uint32_t uSerial; 1050 RTTESTI_CHECK_RC(RTFsQuerySerial(g_szEmptyDir, &uSerial), VINF_SUCCESS); 1051 1052 RTFSPROPERTIES Props; 1053 RTTESTI_CHECK_RC(RTFsQueryProperties(g_szEmptyDir, &Props), VINF_SUCCESS); 1054 1055 RTFSTYPE enmType; 1056 RTTESTI_CHECK_RC(RTFsQueryType(g_szEmptyDir, &enmType), VINF_SUCCESS); 1057 861 1058 } 862 1059 … … 920 1117 921 1118 1119 void fsPerfChSize(void) 1120 { 1121 RTTestISub("chsize"); 1122 1123 /* 1124 * We need some free space to perform this test. 1125 */ 1126 RTFOFF cbFree = 0; 1127 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS); 1128 if (cbFree < _1M) 1129 { 1130 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 1MB", cbFree); 1131 return; 1132 } 1133 1134 /* 1135 * Create a file and play around with it's size. 1136 * We let the current file position follow the end position as we make changes. 1137 */ 1138 RTFILE hFile1; 1139 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file20")), 1140 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS); 1141 uint64_t cbFile = UINT64_MAX; 1142 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS); 1143 RTTESTI_CHECK(cbFile == 0); 1144 1145 uint8_t abBuf[4096]; 1146 static uint64_t const s_acbChanges[] = 1147 { 1148 1023, 1024, 1024, 1025, 8192, 11111, _1M, _8M, _8M, 1149 _4M, _2M + 1, _1M - 1, 65537, 65536, 32768, 8000, 7999, 7998, 1024, 1, 0 1150 }; 1151 uint64_t cbOld = 0; 1152 for (unsigned i = 0; i < RT_ELEMENTS(s_acbChanges); i++) 1153 { 1154 uint64_t cbNew = s_acbChanges[i]; 1155 if (cbNew + _64K >= (uint64_t)cbFree) 1156 continue; 1157 1158 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, cbNew), VINF_SUCCESS); 1159 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS); 1160 RTTESTI_CHECK_MSG(cbFile == cbNew, ("cbFile=%#RX64 cbNew=%#RX64\n", cbFile, cbNew)); 1161 1162 if (cbNew > cbOld) 1163 { 1164 /* Check that the extension is all zeroed: */ 1165 uint64_t cbLeft = cbNew - cbOld; 1166 while (cbLeft > 0) 1167 { 1168 memset(abBuf, 0xff, sizeof(abBuf)); 1169 size_t cbToRead = sizeof(abBuf); 1170 if (cbToRead > cbLeft) 1171 cbToRead = (size_t)cbLeft; 1172 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, cbToRead, NULL), VINF_SUCCESS); 1173 RTTESTI_CHECK(ASMMemIsZero(abBuf, cbToRead)); 1174 cbLeft -= cbToRead; 1175 } 1176 } 1177 else 1178 { 1179 /* Check that reading fails with EOF because current position is now beyond the end: */ 1180 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF); 1181 1182 /* Keep current position at the end of the file: */ 1183 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbNew, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS); 1184 } 1185 cbOld = cbNew; 1186 } 1187 1188 /* 1189 * Profile just the file setting operation itself, keeping the changes within 1190 * an allocation unit to avoid needing to adjust the actual (host) FS allocation. 1191 * ASSUMES allocation unit >= 512 and power of two. 1192 */ 1193 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, _64K), VINF_SUCCESS); 1194 PROFILE_FN(RTFileSetSize(hFile1, _64K - (iIteration & 255) - 128), g_nsTestRun, "RTFileSetSize/noalloc"); 1195 1196 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS); 1197 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS); 1198 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS); 1199 } 1200 1201 1202 int fsPerfIoPrepFile(RTFILE hFile1, uint64_t cbFile, uint8_t **ppbFree) 1203 { 1204 /* 1205 * Seek to the end - 4K and write the last 4K. 1206 * This should have the effect of filling the whole file with zeros. 1207 */ 1208 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, cbFile - _4K, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck); 1209 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, g_abRTZero4K, _4K, NULL), VINF_SUCCESS, rcCheck); 1210 1211 /* 1212 * Check that the space we searched across actually is zero filled. 1213 */ 1214 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck); 1215 size_t cbBuf = _1M; 1216 uint8_t *pbBuf = *ppbFree = (uint8_t *)RTMemAlloc(cbBuf); 1217 RTTESTI_CHECK_RET(pbBuf != NULL, VERR_NO_MEMORY); 1218 uint64_t cbLeft = cbFile; 1219 while (cbLeft > 0) 1220 { 1221 size_t cbToRead = cbBuf; 1222 if (cbToRead > cbLeft) 1223 cbToRead = (size_t)cbLeft; 1224 pbBuf[cbToRead] = 0xff; 1225 1226 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBuf, cbToRead, NULL), VINF_SUCCESS, rcCheck); 1227 RTTESTI_CHECK_RET(ASMMemIsZero(pbBuf, cbToRead), VERR_MISMATCH); 1228 1229 cbLeft -= cbToRead; 1230 } 1231 1232 /* 1233 * Fill the file with 0xf6 and insert offset markers with 1KB intervals. 1234 */ 1235 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck); 1236 memset(pbBuf, 0xf6, cbBuf); 1237 cbLeft = cbFile; 1238 uint64_t off = 0; 1239 while (cbLeft > 0) 1240 { 1241 Assert(!(off & (_1K - 1))); 1242 Assert(!(cbBuf & (_1K - 1))); 1243 for (size_t offBuf = 0; offBuf < cbBuf; offBuf += _1K, off += _1K) 1244 *(uint64_t *)&pbBuf[offBuf] = off; 1245 1246 size_t cbToWrite = cbBuf; 1247 if (cbToWrite > cbLeft) 1248 cbToWrite = (size_t)cbLeft; 1249 1250 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbToWrite, NULL), VINF_SUCCESS, rcCheck); 1251 1252 cbLeft -= cbToWrite; 1253 } 1254 1255 return VINF_SUCCESS; 1256 } 1257 1258 1259 void fsPerfIoSeek(RTFILE hFile1, uint64_t cbFile) 1260 { 1261 /* 1262 * Do a bunch of search tests, most which are random. 1263 */ 1264 struct 1265 { 1266 int rc; 1267 uint32_t uMethod; 1268 int64_t offSeek; 1269 uint64_t offActual; 1270 1271 } aSeeks[9 + 64] = 1272 { 1273 { VINF_SUCCESS, RTFILE_SEEK_BEGIN, 0, 0 }, 1274 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 }, 1275 { VINF_SUCCESS, RTFILE_SEEK_END, 0, cbFile }, 1276 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -4096, cbFile - 4096 }, 1277 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 4096 - (int64_t)cbFile, 0 }, 1278 { VINF_SUCCESS, RTFILE_SEEK_END, -(int64_t)cbFile/2, cbFile / 2 + (cbFile & 1) }, 1279 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -(int64_t)cbFile/2, 0 }, 1280 { VERR_NEGATIVE_SEEK, RTFILE_SEEK_CURRENT, -1, 0 }, 1281 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 }, 1282 }; 1283 1284 uint64_t offActual = 0; 1285 for (unsigned i = 9; i < RT_ELEMENTS(aSeeks); i++) 1286 { 1287 switch (RTRandU32Ex(RTFILE_SEEK_BEGIN, RTFILE_SEEK_END)) 1288 { 1289 default: AssertFailed(); 1290 case RTFILE_SEEK_BEGIN: 1291 aSeeks[i].uMethod = RTFILE_SEEK_BEGIN; 1292 aSeeks[i].rc = VINF_SUCCESS; 1293 aSeeks[i].offSeek = RTRandU64Ex(0, cbFile + cbFile / 8); 1294 aSeeks[i].offActual = offActual = aSeeks[i].offSeek; 1295 break; 1296 1297 case RTFILE_SEEK_CURRENT: 1298 aSeeks[i].uMethod = RTFILE_SEEK_CURRENT; 1299 aSeeks[i].rc = VINF_SUCCESS; 1300 aSeeks[i].offSeek = (int64_t)RTRandU64Ex(0, cbFile + cbFile / 8) - (int64_t)offActual; 1301 aSeeks[i].offActual = offActual += aSeeks[i].offSeek; 1302 break; 1303 1304 case RTFILE_SEEK_END: 1305 aSeeks[i].uMethod = RTFILE_SEEK_END; 1306 aSeeks[i].rc = VINF_SUCCESS; 1307 aSeeks[i].offSeek = -(int64_t)RTRandU64Ex(0, cbFile); 1308 aSeeks[i].offActual = offActual = cbFile + aSeeks[i].offSeek; 1309 break; 1310 } 1311 } 1312 1313 for (unsigned iDoReadCheck = 0; iDoReadCheck < 2; iDoReadCheck++) 1314 { 1315 for (uint32_t i = 0; i < RT_ELEMENTS(aSeeks); i++) 1316 { 1317 offActual = UINT64_MAX; 1318 int rc = RTFileSeek(hFile1, aSeeks[i].offSeek, aSeeks[i].uMethod, &offActual); 1319 if (rc != aSeeks[i].rc) 1320 RTTestIFailed("Seek #%u: Expected %Rrc, got %Rrc", i, aSeeks[i].rc, rc); 1321 if (RT_SUCCESS(rc) && offActual != aSeeks[i].offActual) 1322 RTTestIFailed("Seek #%u: offActual %#RX64, expected %#RX64", i, offActual, aSeeks[i].offActual); 1323 if (RT_SUCCESS(rc)) 1324 { 1325 uint64_t offTell = RTFileTell(hFile1); 1326 if (offTell != offActual) 1327 RTTestIFailed("Seek #%u: offActual %#RX64, RTFileTell %#RX64", i, offActual, offTell); 1328 } 1329 1330 if (RT_SUCCESS(rc) && offActual + _2K <= cbFile && iDoReadCheck) 1331 { 1332 uint8_t abBuf[_2K]; 1333 RTTESTI_CHECK_RC(rc = RTFileRead(hFile1, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS); 1334 if (RT_SUCCESS(rc)) 1335 { 1336 size_t offMarker = (size_t)(RT_ALIGN_64(offActual, _1K) - offActual); 1337 uint64_t uMarker = *(uint64_t *)&abBuf[offMarker]; /** @todo potentially unaligned access */ 1338 if (uMarker != offActual + offMarker) 1339 RTTestIFailed("Seek #%u: Invalid marker value (@ %#RX64): %#RX64, expected %#RX64", 1340 i, offActual, uMarker, offActual + offMarker); 1341 1342 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -(int64_t)sizeof(abBuf), RTFILE_SEEK_CURRENT, NULL), VINF_SUCCESS); 1343 } 1344 } 1345 } 1346 } 1347 1348 1349 /* 1350 * Profile seeking relative to the beginning of the file and relative 1351 * to the end. The latter might be more expensive in a SF context. 1352 */ 1353 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? iIteration : iIteration % cbFile, RTFILE_SEEK_BEGIN, NULL), 1354 g_nsTestRun, "RTFileSeek/BEGIN"); 1355 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? -(int64_t)iIteration : -(int64_t)(iIteration % cbFile), RTFILE_SEEK_END, NULL), 1356 g_nsTestRun, "RTFileSeek/END"); 1357 1358 } 1359 1360 1361 DECL_FORCE_INLINE(int) fsPerfIoReadWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock, 1362 uint64_t *poffActual, uint32_t *pcSeeks) 1363 { 1364 /* Do we need to seek back to the start? */ 1365 if (*poffActual + cbBlock <= cbFile) 1366 { /* likely */ } 1367 else 1368 { 1369 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck); 1370 *pcSeeks += 1; 1371 *poffActual = 0; 1372 } 1373 1374 size_t cbActuallyRead = 0; 1375 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBlock, cbBlock, &cbActuallyRead), VINF_SUCCESS, rcCheck); 1376 if (cbActuallyRead == cbBlock) 1377 { 1378 *poffActual += cbActuallyRead; 1379 return VINF_SUCCESS; 1380 } 1381 RTTestIFailed("RTFileRead at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyRead, cbBlock); 1382 *poffActual += cbActuallyRead; 1383 return VERR_READ_ERROR; 1384 } 1385 1386 1387 void fsPerfIoRead(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t **ppbFree) 1388 { 1389 RTTestISubF("read %RU32", cbBlock); 1390 1391 uint8_t *pbBuf = *ppbFree = (uint8_t *)RTMemPageAlloc(cbBlock); 1392 if (!pbBuf) 1393 { 1394 RTTestSkipped(g_hTest, "insufficient (virtual) memory available"); 1395 return; 1396 } 1397 1398 char szDesc[64]; 1399 RTStrPrintf(szDesc, sizeof(szDesc), "RTFileRead/seq/%RU32", cbBlock); 1400 1401 RTTESTI_CHECK_RC_RETV(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS); 1402 uint64_t offActual = 0; 1403 uint32_t cSeeks = 0; 1404 1405 /* Estimate how many iterations we need to fill up the given timeslot: */ 1406 fsPerfYield(); 1407 uint64_t nsStart = RTTimeNanoTS(); 1408 uint64_t ns; 1409 do 1410 ns = RTTimeNanoTS(); 1411 while (ns == nsStart); 1412 nsStart = ns; 1413 1414 uint32_t iIteration = 0; 1415 do 1416 { 1417 RTTESTI_CHECK_RC_RETV(fsPerfIoReadWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks), VINF_SUCCESS); 1418 iIteration++; 1419 ns = RTTimeNanoTS() - nsStart; 1420 } while (ns < RT_NS_10MS); 1421 ns /= iIteration; 1422 if (ns > g_nsPerNanoTSCall) 1423 ns -= g_nsPerNanoTSCall; 1424 uint32_t const cIterations = g_nsTestRun / ns; 1425 1426 /* Do the actual profiling: */ 1427 cSeeks = 0; 1428 fsPerfYield(); 1429 nsStart = RTTimeNanoTS(); 1430 for (iIteration = 0; iIteration < cIterations; iIteration++) 1431 { 1432 fsPerfIoReadWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks); 1433 } 1434 ns = RTTimeNanoTS() - nsStart; 1435 RTTestIValueF(ns / iIteration, 1436 RTTESTUNIT_NS_PER_OCCURRENCE, "RTFileRead/seq/%RU32 latency", cbBlock); 1437 RTTestIValueF((uint64_t)iIteration * cbBlock / ((double)ns / RT_NS_1SEC), 1438 RTTESTUNIT_BYTES_PER_SEC, "RTFileRead/seq/%RU32 throughput", cbBlock); 1439 RTTestIValueF(iIteration, 1440 RTTESTUNIT_CALLS, "RTFileRead/seq/%RU32 calls", cbBlock); 1441 RTTestIValueF((uint64_t)iIteration * cbBlock, 1442 RTTESTUNIT_BYTES, "RTFileRead/seq/%RU32 bytes", cbBlock); 1443 RTTestIValueF(cSeeks, 1444 RTTESTUNIT_OCCURRENCES, "RTFileRead/seq/%RU32 seeks", cbBlock); 1445 } 1446 1447 1448 void fsPerfIoWrite(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t **ppbFree) 1449 { 1450 RT_NOREF(hFile1, cbFile, cbBlock, ppbFree); 1451 } 1452 1453 1454 /** 1455 * This does the read, write and seek tests. 1456 */ 1457 void fsPerfIo(void) 1458 { 1459 RTTestISub("I/O"); 1460 1461 /* 1462 * Determin the size of the test file. 1463 */ 1464 g_szDir[g_cchDir] = '\0'; 1465 RTFOFF cbFree = 0; 1466 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS); 1467 uint64_t cbFile = g_cbIoFile; 1468 if (cbFile + _16M < (uint64_t)cbFree) 1469 cbFile = RT_ALIGN_64(cbFile, _64K); 1470 else 1471 { 1472 if (cbFree < _32M) 1473 { 1474 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree); 1475 return; 1476 } 1477 cbFile = cbFree - (cbFree > _128M ? _64M : _16M); 1478 cbFile = RT_ALIGN_64(cbFile, _64K); 1479 RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree); 1480 } 1481 if (cbFile < _64K) 1482 { 1483 RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 64KB", cbFile); 1484 return; 1485 } 1486 1487 /* 1488 * Create a cbFile sized test file. 1489 */ 1490 RTFILE hFile1; 1491 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file21")), 1492 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS); 1493 uint8_t *pbFree = NULL; 1494 int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree); 1495 RTMemFree(pbFree); 1496 if (RT_SUCCESS(rc)) 1497 { 1498 /* 1499 * Do the testing & profiling. 1500 */ 1501 if (g_fSeek) 1502 fsPerfIoSeek(hFile1, cbFile); 1503 if (g_fRead) 1504 for (unsigned i = 0; i < g_cIoBlocks; i++) 1505 { 1506 pbFree = NULL; 1507 fsPerfIoRead(hFile1, cbFile, g_acbIoBlocks[i], &pbFree); 1508 RTMemPageFree(pbFree, g_acbIoBlocks[i]); 1509 } 1510 if (g_fWrite) 1511 for (unsigned i = 0; i < g_cIoBlocks; i++) 1512 { 1513 pbFree = NULL; 1514 fsPerfIoWrite(hFile1, cbFile, g_acbIoBlocks[i], &pbFree); 1515 RTMemPageFree(pbFree, g_acbIoBlocks[i]); 1516 } 1517 } 1518 1519 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS); 1520 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS); 1521 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS); 1522 } 1523 1524 922 1525 static void Usage(PRTSTREAM pStrm) 923 1526 { … … 928 1531 RTStrmPrintf(pStrm, "options: \n"); 929 1532 930 931 1533 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++) 932 1534 { 1535 char szHelp[80]; 933 1536 const char *pszHelp; 934 1537 switch (g_aCmdOptions[i].iShort) 935 1538 { 936 1539 case 'd': pszHelp = "The directory to use for testing. Default: CWD/fstestdir"; break; 937 case 's': pszHelp = "Benchmark duration. Default: 10 sec"; break; 1540 case 'e': pszHelp = "Enables all tests. Default: -e"; break; 1541 case 'z': pszHelp = "Disables all tests. Default: -e"; break; 1542 case 's': pszHelp = "Set benchmark duration in seconds. Default: 10 sec"; break; 1543 case 'm': pszHelp = "Set benchmark duration in milliseconds. Default: 10000 ms"; break; 938 1544 case 'v': pszHelp = "More verbose execution."; break; 939 1545 case 'q': pszHelp = "Quiet execution."; break; … … 941 1547 case 'V': pszHelp = "Displays the program revision"; break; 942 1548 default: 943 pszHelp = "Option undocumented"; 1549 if (g_aCmdOptions[i].iShort >= kCmdOpt_First) 1550 { 1551 if (RTStrStartsWith(g_aCmdOptions[i].pszLong, "--no-")) 1552 RTStrPrintf(szHelp, sizeof(szHelp), "Disables the '%s' test.", g_aCmdOptions[i].pszLong + 5); 1553 else 1554 RTStrPrintf(szHelp, sizeof(szHelp), "Enables the '%s' test.", g_aCmdOptions[i].pszLong + 2); 1555 pszHelp = szHelp; 1556 } 1557 else 1558 pszHelp = "Option undocumented"; 944 1559 break; 945 1560 } 946 char szOpt[256]; 947 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort); 948 RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp); 1561 if ((unsigned)g_aCmdOptions[i].iShort < 127U) 1562 { 1563 char szOpt[64]; 1564 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort); 1565 RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp); 1566 } 1567 else 1568 RTStrmPrintf(pStrm, " %-20s%s\n", g_aCmdOptions[i].pszLong, pszHelp); 949 1569 } 950 1570 } … … 998 1618 g_nsTestRun = ValueUnion.u32 * RT_NS_1SEC_64; 999 1619 break; 1620 1621 case 'm': 1622 if (ValueUnion.u64 == 0) 1623 g_nsTestRun = RT_NS_1SEC_64 * 10; 1624 else 1625 g_nsTestRun = ValueUnion.u64 * RT_NS_1MS; 1626 break; 1627 1628 case 'e': 1629 g_fManyFiles = true; 1630 g_fOpen = true; 1631 g_fFStat = true; 1632 g_fFChMod = true; 1633 g_fFUtimes = true; 1634 g_fStat = true; 1635 g_fChMod = true; 1636 g_fUtimes = true; 1637 g_fRename = true; 1638 g_fDirEnum = true; 1639 g_fMkRmDir = true; 1640 g_fStatVfs = true; 1641 g_fRm = true; 1642 g_fChSize = true; 1643 g_fRead = true; 1644 g_fWrite = true; 1645 g_fSeek = true; 1646 break; 1647 1648 case 'z': 1649 g_fManyFiles = false; 1650 g_fOpen = false; 1651 g_fFStat = false; 1652 g_fFChMod = false; 1653 g_fFUtimes = false; 1654 g_fStat = false; 1655 g_fChMod = false; 1656 g_fUtimes = false; 1657 g_fRename = false; 1658 g_fDirEnum = false; 1659 g_fMkRmDir = false; 1660 g_fStatVfs = false; 1661 g_fRm = false; 1662 g_fChSize = false; 1663 g_fRead = false; 1664 g_fWrite = false; 1665 g_fSeek = false; 1666 break; 1667 1668 #define CASE_OPT(a_Stem) \ 1669 case RT_CONCAT(kCmdOpt_,a_Stem): RT_CONCAT(g_f,a_Stem) = true; break; \ 1670 case RT_CONCAT(kCmdOpt_No,a_Stem): RT_CONCAT(g_f,a_Stem) = false; break 1671 CASE_OPT(ManyFiles); 1672 CASE_OPT(Open); 1673 CASE_OPT(FStat); 1674 CASE_OPT(FChMod); 1675 CASE_OPT(FUtimes); 1676 CASE_OPT(Stat); 1677 CASE_OPT(ChMod); 1678 CASE_OPT(Utimes); 1679 CASE_OPT(Rename); 1680 CASE_OPT(DirEnum); 1681 CASE_OPT(MkRmDir); 1682 CASE_OPT(StatVfs); 1683 CASE_OPT(Rm); 1684 CASE_OPT(ChSize); 1685 CASE_OPT(Seek); 1686 CASE_OPT(Read); 1687 CASE_OPT(Write); 1688 #undef CASE_OPT 1000 1689 1001 1690 case 'q': … … 1061 1750 if (RT_SUCCESS(rc)) 1062 1751 { 1063 //g_fManyFiles = false; 1752 /* Profile RTTimeNanoTS(). */ 1753 fsPerfNanoTS(); 1064 1754 1065 1755 /* Do tests: */ 1066 1756 if (g_fManyFiles) 1067 1757 fsPerfManyFiles(); 1068 #if 1 1069 fsPerfOpen(); 1070 fsPerfFStat(); 1071 fsPerfFChMod(); 1072 fsPerfFUtimes(); 1073 fsPerfStat(); 1074 fsPerfChmod(); 1075 fsPerfUtimes(); 1076 fsPerfRename(); 1077 vsPerfDirEnum(); 1078 #endif 1079 fsPerfMkDirRmDir(); 1080 /// @todo fsPerfRead 1081 /// @todo fsPerfWrite 1082 /// @todo fsPerfSeek 1083 /// @todo fsPerfChSize 1084 fsPerfRm(); /* must come last as it deletes manyfiles and manytree */ 1758 if (g_fOpen) 1759 fsPerfOpen(); 1760 if (g_fFStat) 1761 fsPerfFStat(); 1762 if (g_fFChMod) 1763 fsPerfFChMod(); 1764 if (g_fFUtimes) 1765 fsPerfFUtimes(); 1766 if (g_fStat) 1767 fsPerfStat(); 1768 if (g_fChMod) 1769 fsPerfChmod(); 1770 if (g_fUtimes) 1771 fsPerfUtimes(); 1772 if (g_fRename) 1773 fsPerfRename(); 1774 if (g_fDirEnum) 1775 vsPerfDirEnum(); 1776 if (g_fMkRmDir) 1777 fsPerfMkRmDir(); 1778 if (g_fStatVfs) 1779 fsPerfStatVfs(); 1780 if (g_fRm || g_fManyFiles) 1781 fsPerfRm(); /* deletes manyfiles and manytree */ 1782 if (g_fChSize) 1783 fsPerfChSize(); 1784 if (g_fRead || g_fWrite || g_fSeek) 1785 fsPerfIo(); 1085 1786 } 1086 1787 }
Note:
See TracChangeset
for help on using the changeset viewer.