DiskBenchmark.Mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. MODULE DiskBenchmark; (** AUTHOR "staubesv"; PURPOSE "Simple block device benchmark"; *)
  2. (**
  3. * Usage:
  4. *
  5. * DiskBenchmark.Bench ~ will start benchmark with default parameters (only read)
  6. * WMPartitions.Open ~ will open the graphical front-end. This benchmark can be found in Partitions -> Benchmark
  7. *
  8. * History:
  9. *
  10. * 01.12.2005 History started (staubesv)
  11. * 12.12.2005 Open/Close Disks.Device moved to PartitionsLib.Operation (staubesv)
  12. * 16.12.2005 Added doRead parameter (staubesv)
  13. *)
  14. IMPORT
  15. Streams, Commands, Random, Kernel, Disks, Partitions, Lib := PartitionsLib, Strings;
  16. CONST
  17. (* Allow write tests -> very, very dangerous!!! *)
  18. AllowWrite = TRUE;
  19. TYPE
  20. DiskBench* = OBJECT(Lib.Operation)
  21. VAR
  22. (* parameters *)
  23. doRandom : BOOLEAN; (* Perform random block benchmark? *)
  24. doSequential : BOOLEAN; (* Perform sequential block benchmark? *)
  25. doRead : BOOLEAN; (* Perform read benchmarks ? *)
  26. doWrite : BOOLEAN; (* Perform write benchmarks? *)
  27. nbrOfBlocks : LONGINT; (* Number of blocks used in random block benchmark *)
  28. blocksizes : SET; (* Which block sizes should be benchmarked? *)
  29. (* progress status *)
  30. cur : HUGEINT; (* how many sectors have been processed *)
  31. max : HUGEINT; (* how many sectors must be processed in total *)
  32. (* AosDiskdevice information *)
  33. start : LONGINT; (* First sector in partition *)
  34. size : LONGINT; (* partition size in sectors *)
  35. buffer : POINTER TO ARRAY OF CHAR;
  36. random : Random.Generator;
  37. PROCEDURE SetParameters*(doRandom, doSequential, doRead, doWrite : BOOLEAN; nbrOfBlocks : LONGINT; blocksizes : SET);
  38. BEGIN
  39. SELF.doRandom := doRandom; SELF.doSequential := doSequential; SELF.doRead := doRead;
  40. IF ~AllowWrite THEN doWrite := FALSE; ELSE SELF.doWrite := doWrite; END;
  41. SELF.nbrOfBlocks := nbrOfBlocks; SELF.blocksizes := blocksizes;
  42. END SetParameters;
  43. PROCEDURE ValidParameters*() : BOOLEAN;
  44. BEGIN
  45. IF ~doRead & ~doWrite THEN ReportError("Wrong Parameter: Neither read nor write benchmark?"); RETURN FALSE; END;
  46. IF doRandom & (nbrOfBlocks <= 0) THEN ReportError("Wrong Parameter: nbrOfBlocks < 1"); RETURN FALSE; END;
  47. IF blocksizes = {} THEN ReportError("Wrong Parameter: No blocksize selected"); RETURN FALSE; END;
  48. IF doWrite THEN locktype := Lib.WriterLock; ELSE locktype := Lib.ReaderLock; END;
  49. RETURN TRUE;
  50. END ValidParameters;
  51. PROCEDURE GetNbrOfSectors(blocksize : LONGINT) : LONGINT;
  52. VAR result : LONGINT;
  53. BEGIN
  54. result := -1;
  55. IF blocksize MOD disk.device.blockSize = 0 THEN result := blocksize DIV disk.device.blockSize; END;
  56. RETURN result;
  57. END GetNbrOfSectors;
  58. PROCEDURE DoOperation*;
  59. VAR sectors, blocksize, maxBlocksize, i : LONGINT;
  60. BEGIN
  61. (* General information *)
  62. info.String("Benchmark settings: "); info.Ln;
  63. info.String(" Random: ");
  64. IF doRandom THEN info.String("Yes ("); info.Int(nbrOfBlocks, 0); info.String(" blocks per blocksize) "); info.Ln;
  65. ELSE info.String("No"); info.Ln;
  66. END;
  67. info.String(" Sequential: "); IF doSequential THEN info.String("Yes"); ELSE info.String("No"); END; info.Ln;
  68. info.String(" Read Tests: "); IF doRead THEN info.String("Yes"); ELSE info.String("No"); END; info.Ln;
  69. info.String(" Write Tests: ");
  70. IF doWrite THEN
  71. info.String("Yes");
  72. IF ~AllowWrite THEN info.String(" (But disabled in DiskBenchmark.DoWrite)"); END;
  73. ELSE
  74. info.String("No");
  75. END; info.Ln;
  76. info.String(" Device sector size: "); info.Int(disk.device.blockSize, 0); info.Ln; info.Ln;
  77. info.String("Benchmark results: "); info.Ln;
  78. start := disk.table[partition].start; size := disk.table[partition].size;
  79. (* Compute amount of work to be done in number of sectors to be read/written (for progress status) *)
  80. cur := 0; max := 0; blocksize := 512;
  81. FOR i := 0 TO MAX(SET) DO
  82. IF i IN blocksizes THEN
  83. sectors := GetNbrOfSectors(blocksize);
  84. IF sectors > 0 THEN
  85. IF doRandom THEN max := max + nbrOfBlocks * sectors; END;
  86. IF doSequential THEN max := max + (size DIV sectors) * sectors; END;
  87. END;
  88. IF blocksize > maxBlocksize THEN maxBlocksize := blocksize; END;
  89. END;
  90. blocksize := blocksize * 2;
  91. END;
  92. IF doWrite & doRead THEN max := max * 2; END;
  93. (* Allocate buffer on heap which can hold the largest transfer used in the test *)
  94. NEW(buffer, maxBlocksize);
  95. IF alive & doRandom THEN
  96. NEW(random); random.InitSeed(Kernel.GetTicks());
  97. blocksize := 512; i := 0;
  98. LOOP
  99. IF i IN blocksizes THEN
  100. IF doRead THEN PerformRandomBench(Disks.Read, nbrOfBlocks, blocksize); END;
  101. IF doWrite THEN PerformRandomBench(Disks.Write, nbrOfBlocks, blocksize); END;
  102. END;
  103. blocksize := blocksize * 2;
  104. INC(i);
  105. IF ~alive OR (i > MAX(SET)) THEN EXIT; END;
  106. END;
  107. END;
  108. IF alive & doSequential THEN
  109. blocksize := 512; i := 0;
  110. LOOP
  111. IF i IN blocksizes THEN
  112. IF doRead THEN PerformSequentialBench(Disks.Read, blocksize); END;
  113. IF doWrite THEN PerformSequentialBench(Disks.Write, blocksize); END;
  114. END;
  115. blocksize := blocksize * 2;
  116. INC(i);
  117. IF ~alive OR (i > MAX(SET)) THEN EXIT; END;
  118. END;
  119. END;
  120. result.String("Benchmark on partition "); result.String(diskpartString);
  121. IF alive THEN
  122. result.String(" finished.");
  123. ELSE
  124. result.String(" aborted.");
  125. END;
  126. END DoOperation;
  127. (* Read/Write 'nbrOfBlocks' of the specified 'blocksize' *)
  128. PROCEDURE PerformRandomBench(mode, nbrOfBlocks, blocksize : LONGINT);
  129. VAR
  130. string :Lib.String;
  131. block, sectors : LONGINT;
  132. nbrOfBytes : HUGEINT;
  133. avgDuration : REAL;
  134. milliTimer : Kernel.MilliTimer;
  135. time : LONGINT;
  136. i : LONGINT; res : WORD;
  137. temp : ARRAY 256 OF CHAR;
  138. BEGIN
  139. ASSERT((mode = Disks.Read) OR (mode = Disks.Write));
  140. ASSERT(random # NIL);
  141. IF blocksize MOD disk.device.blockSize = 0 THEN
  142. sectors := blocksize DIV disk.device.blockSize;
  143. string := "Random Block ";
  144. IF mode = Disks.Read THEN Strings.Append(string, "Read Test (");
  145. ELSE Strings.Append(string, "Write Test (");
  146. END;
  147. WriteB(blocksize, temp); Strings.Append(string, temp); Strings.Append(string, ")");
  148. SetStatus(state.status, string, 0, cur, max, TRUE);
  149. (* Perform benchmark for a single block size *)
  150. Kernel.SetTimer(milliTimer, 0);
  151. i := 1;
  152. LOOP
  153. block := random.Dice(size - sectors);
  154. disk.device.Transfer(mode, start + block, sectors, buffer^, 0, res);
  155. IF res # Disks.Ok THEN Lib.GetTransferError(disk.device, mode, start + block, res, temp); ReportError(temp); END;
  156. cur := cur + sectors;
  157. SetCurrentProgress(cur);
  158. INC(i);
  159. IF (i > nbrOfBlocks) OR (res # Disks.Ok) OR ~alive THEN EXIT; END;
  160. END;
  161. (* Evaluation *)
  162. IF alive THEN (* benchmark has not been aborted *)
  163. time := Kernel.Elapsed(milliTimer); (* duration of test in milliseconds *)
  164. IF time <= 0 THEN time := 1; END; (* prevent division by zero *)
  165. nbrOfBytes := blocksize * nbrOfBlocks; (* bytes read/write in this time period *)
  166. info.String(string); info.String(": ");
  167. WriteK(ENTIER(nbrOfBytes / 1024 / (time / 1000)), temp); info.String(temp); info.String("/s");
  168. avgDuration := time / nbrOfBlocks;
  169. info.String(" (Avg. "); info.Int(ENTIER(avgDuration), 0); info.Char("."); info.Int(ENTIER(100*(avgDuration - ENTIER(avgDuration))), 0);
  170. info.String("ms per block)"); info.Ln;
  171. END;
  172. ELSE
  173. info.String("Skip "); WriteB(blocksize, temp); info.String(temp); info.String(" test: Blocksize is not multiple of sector size"); info.Ln;
  174. END;
  175. END PerformRandomBench;
  176. (* Sequentially Read/Write all blocks of the block device using 'blocksize' blocks *)
  177. PROCEDURE PerformSequentialBench(mode, blocksize : LONGINT);
  178. VAR
  179. string : Lib.String;
  180. block, sectors : LONGINT;
  181. nbrOfBytes : HUGEINT;
  182. milliTimer : Kernel.MilliTimer;
  183. time : LONGINT;
  184. i : LONGINT; res : WORD;
  185. temp : ARRAY 256 OF CHAR;
  186. BEGIN
  187. ASSERT((mode = Disks.Read) OR (mode = Disks.Write));
  188. IF blocksize MOD disk.device.blockSize = 0 THEN
  189. sectors := blocksize DIV disk.device.blockSize;
  190. string := "Sequential Block";
  191. IF mode = Disks.Read THEN Strings.Append(string, "Read Test (");
  192. ELSE Strings.Append(string, "Write Test (");
  193. END;
  194. WriteB(blocksize, temp); Strings.Append(string, temp); Strings.Append(string, ")");
  195. SetStatus(state.status, string, 0, cur, max, TRUE);
  196. (* Perform benchmark for a single block size *)
  197. Kernel.SetTimer(milliTimer, 0);
  198. i := 0; block := 0;
  199. LOOP
  200. block := i * sectors;
  201. IF block + sectors > size THEN EXIT; END;
  202. disk.device.Transfer(mode, start + block, sectors, buffer^, 0, res);
  203. IF res # Disks.Ok THEN Lib.GetTransferError(disk.device, mode, start + block, res, temp); ReportError(temp); END;
  204. cur := cur + sectors;
  205. SetCurrentProgress(cur);
  206. INC(i);
  207. IF (res # Disks.Ok) OR ~alive THEN alive := FALSE; EXIT; END;
  208. END;
  209. (* Evaluation *)
  210. IF alive THEN (* benchmark has not been aborted *)
  211. time := Kernel.Elapsed(milliTimer); (* duration of test in milliseconds *)
  212. nbrOfBytes := (i+1) * blocksize; (* bytes read/write in this time period *)
  213. info.String(string); info.String(": ");
  214. WriteK(ENTIER(nbrOfBytes / 1024 / (time / 1000)), temp); info.String(temp); info.String("/s"); info.Ln;
  215. END;
  216. ELSE
  217. info.String("Skip "); WriteB(blocksize, temp); info.String(temp); info.String(" test: Blocksize is not multiple of sector size"); info.Ln;
  218. END;
  219. END PerformSequentialBench;
  220. PROCEDURE WriteB*(k: LONGINT; VAR string: ARRAY OF CHAR);
  221. VAR suffix: ARRAY 3 OF CHAR;
  222. BEGIN
  223. IF k < 1024 THEN suffix := "B";
  224. ELSE k := k DIV 1024; suffix := "KB";
  225. END;
  226. Strings.IntToStr(SHORT(k), string); Strings.Append(string, suffix);
  227. END WriteB;
  228. PROCEDURE WriteK*(k: LONGINT; VAR string: ARRAY OF CHAR);
  229. VAR suffix: ARRAY 3 OF CHAR;
  230. BEGIN
  231. IF k < 100* 1024 THEN suffix := "KB";
  232. ELSE k := k DIV 1024; suffix := "MB";
  233. END;
  234. Strings.IntToStr(k, string); Strings.Append(string, suffix);
  235. END WriteK;
  236. PROCEDURE &Init*(disk :Lib.Disk; partition : LONGINT; out : Streams.Writer);
  237. BEGIN
  238. Init^(disk, partition, out);
  239. name := "DiskBenchmark"; desc := "Perform disk benchmark on partition"; locktype := Lib.WriterLock;
  240. END Init;
  241. END DiskBench;
  242. (** Perform a benchmark on partition *)
  243. PROCEDURE Bench*(context : Commands.Context); (** dev#part *)
  244. VAR
  245. selection : Lib.Selection;
  246. bench : DiskBench;
  247. BEGIN
  248. IF Partitions.GetSelection(context, FALSE, selection) THEN
  249. NEW(bench, selection.disk, selection.partition, context.out);
  250. bench.SetParameters(TRUE, TRUE, TRUE, FALSE, 100, {0..11});
  251. bench.SetStart;
  252. context.out.String("Partitions: UID "); context.out.Int(bench.uid, 0); context.out.String(": Started Benchmark on ");
  253. context.out.String(selection.disk.device.name); context.out.Char("#"); context.out.Int(selection.partition, 0); context.out.Ln;
  254. ELSE (* skip; error written to <w> by ScanOpenPart *)
  255. END;
  256. END Bench;
  257. END DiskBenchmark.
  258. DiskBenchmark.Bench USB2#1 ~ System.Free DiskBenchmark ~
  259. Partitions.ShowOps ~
  260. Partitions.ShowOps detail ~