SHOW:
|
|
- or go back to the newest paste.
1 | package frans; | |
2 | ||
3 | /** | |
4 | * Class to test access time (latency) for cache and RAM. | |
5 | * Sequential access and random access of values in a long[] array. | |
6 | * | |
7 | * @author Frans Lundberg | |
8 | */ | |
9 | public class MemoryAccess { | |
10 | static final int SIZE_CACHE = 100000; // 100003 is prime. Total: 0.8 MB. | |
11 | static final long REPS1_CACHE = 10 * 1000; // for seq | |
12 | static final long REPS2_CACHE = 100 * 1000000; // for random | |
13 | static final int SIZE_RAM = 500 * SIZE_CACHE; // 400 MB of data | |
14 | static final long REPS1_RAM = 10; // for seq | |
15 | static final long REPS2_RAM = 10 * 1000000; // for random | |
16 | ||
17 | /** | |
18 | * Sequential memory accesses to a long[] array. | |
19 | * 'size' must be SIZE_CACHE or SIZE_RAM. | |
20 | */ | |
21 | Result seq(final int size) { | |
22 | long[] data = new long[size]; | |
23 | ||
24 | for (int i = 0; i < data.length; i++) { | |
25 | data[i] = 1; | |
26 | } | |
27 | ||
28 | long t0 = 0; | |
29 | long sum = 0; | |
30 | long count; | |
31 | ||
32 | t0 = System.nanoTime(); | |
33 | ||
34 | if (size == SIZE_CACHE) { | |
35 | count = SIZE_CACHE * REPS1_CACHE; | |
36 | for (int k = 0; k < REPS1_CACHE; k++) { | |
37 | for (int i = 0; i < SIZE_CACHE; i++) { | |
38 | sum += data[i]; | |
39 | } | |
40 | } | |
41 | } else if (size == SIZE_RAM) { | |
42 | count = SIZE_RAM * REPS1_RAM; | |
43 | for (int k = 0; k < REPS1_RAM; k++) { | |
44 | for (int i = 0; i < SIZE_RAM; i++) { | |
45 | sum += data[i]; | |
46 | } | |
47 | } | |
48 | } else { | |
49 | throw new Error("Bad size: " + size); | |
50 | } | |
51 | ||
52 | double time = (System.nanoTime() - t0) * 1e-9; | |
53 | return new Result(count, time, sum); | |
54 | } | |
55 | ||
56 | ||
57 | /** | |
58 | * Random memory access to a long[] array. | |
59 | * 'size' must be SIZE_CACHE or SIZE_RAM. | |
60 | */ | |
61 | Result random(final int size) { | |
62 | long[] data = new long[size]; | |
63 | long t0; | |
64 | long sum = 0; | |
65 | long index = 0; | |
66 | long count; | |
67 | ||
68 | for (int i = 0; i < size; i++) { | |
69 | data[i] = 1L; | |
70 | } | |
71 | ||
72 | t0 = System.nanoTime(); | |
73 | ||
74 | if (size == SIZE_CACHE) { | |
75 | count = REPS2_CACHE; | |
76 | for (int k = 0; k < REPS2_CACHE; k++) { | |
77 | sum += data[(int)(index % SIZE_CACHE)]; | |
78 | index += 836413; | |
79 | } | |
80 | } else if (size == SIZE_RAM) { | |
81 | count = REPS2_RAM; | |
82 | for (int k = 0; k < REPS2_RAM; k++) { | |
83 | sum += data[(int)(index % SIZE_RAM)]; | |
84 | index += 836413; | |
85 | } | |
86 | } else { | |
87 | throw new Error("Bad size: " + size); | |
88 | } | |
89 | ||
90 | double time = (System.nanoTime() - t0) * 1e-9; | |
91 | return new Result(count, time, sum); | |
92 | } | |
93 | ||
94 | void runTest(String name, TestMethod test) { | |
95 | int reps = 8; | |
96 | double minTime = Double.MAX_VALUE; | |
97 | Result winner = null; | |
98 | ||
99 | System.out.println("---- " + name + " ----"); | |
100 | ||
101 | for (int i = 0; i < reps; i++) { | |
102 | Result res = test.run(); | |
103 | if (res.time < minTime) { | |
104 | winner = res; | |
105 | } | |
106 | } | |
107 | ||
108 | System.out.println(winner.toString()); | |
109 | } | |
110 | ||
111 | /** To store the result. */ | |
112 | static class Result { | |
113 | long count; | |
114 | long sum; | |
115 | double time; | |
116 | ||
117 | Result(long count, double time, long sum) { | |
118 | this.count = count; | |
119 | this.time = time; | |
120 | this.sum = sum; | |
121 | } | |
122 | ||
123 | public String toString() { | |
124 | double nanos = (time * 1e9) / count; | |
125 | return String.format("Access time %.2f ns, time: %.3f s (sum:%d).\n", nanos, time, sum); | |
126 | } | |
127 | } | |
128 | ||
129 | static interface TestMethod { | |
130 | public Result run(); | |
131 | } | |
132 | ||
133 | void go() { | |
134 | runTest("cacheSeq", new TestMethod() { | |
135 | public Result run() { | |
136 | return seq(SIZE_CACHE); | |
137 | } | |
138 | }); | |
139 | ||
140 | runTest("cacheRan", new TestMethod() { | |
141 | public Result run() { | |
142 | return random(SIZE_CACHE); | |
143 | } | |
144 | }); | |
145 | ||
146 | runTest("ramSeq", new TestMethod() { | |
147 | public Result run() { | |
148 | return seq(SIZE_RAM); | |
149 | } | |
150 | }); | |
151 | ||
152 | runTest("ramRan", new TestMethod() { | |
153 | public Result run() { | |
154 | return random(SIZE_RAM); | |
155 | } | |
156 | }); | |
157 | } | |
158 | ||
159 | public static void main(String[] args) { | |
160 | new MemoryAccess().go(); | |
161 | } | |
162 | } | |
163 | ||
164 | /* | |
165 | 131106 | |
166 | ====== | |
167 | ||
168 | - | "sequential" / "random" access are better terms. Now used. |
168 | + | |
169 | - | Refactored. Result: |
169 | + | |
170 | cacheRan 3.16 ns, 6.64 cycles | |
171 | ramSeq 0.56 ns, 1.37 cycles | |
172 | ramRan 20.07 ns, 42.16 cycles | |
173 | - | cacheRan 4.39 ns, 9.20 cycles |
173 | + | |
174 | On my computer "Cissi": | |
175 | Ubuntu 12.04, CPU: Intel(R) Core(TM) i7-3687U CPU @ 2.10GHz, Cache: 4096 KiB. | |
176 | Java version: | |
177 | Oracle JVM 1.7.0_25. | |
178 | Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode) | |
179 | ||
180 | sudo cat /proc/cpuinfo | grep "cache" --> | |
181 | cache size : 4096 KB | |
182 | sudo cat /proc/cpuinfo | fgrep 'cpu MHz' | |
183 | Max freq: cpu MHz : 2101.000 <--> 0.476 ns / cycle | |
184 | */ |