Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(215)

Side by Side Diff: cc/ipc/cc_serialization_perftest.cc

Issue 2657873003: cc: Add more seriliazation perf test cases (Closed)
Patch Set: add comments according to review comments Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <utility> 5 #include <utility>
6 6
7 #include "base/test/launcher/unit_test_launcher.h" 7 #include "base/test/launcher/unit_test_launcher.h"
8 #include "base/test/test_suite.h" 8 #include "base/test/test_suite.h"
9 #include "cc/ipc/cc_param_traits.h" 9 #include "cc/ipc/cc_param_traits.h"
10 #include "cc/ipc/compositor_frame.mojom.h" 10 #include "cc/ipc/compositor_frame.mojom.h"
(...skipping 26 matching lines...) Expand all
37 37
38 enum class UseSingleSharedQuadState { YES, NO }; 38 enum class UseSingleSharedQuadState { YES, NO };
39 39
40 class CCSerializationPerfTest : public testing::Test { 40 class CCSerializationPerfTest : public testing::Test {
41 protected: 41 protected:
42 static bool ReadMessage(const IPC::Message* msg, CompositorFrame* frame) { 42 static bool ReadMessage(const IPC::Message* msg, CompositorFrame* frame) {
43 base::PickleIterator iter(*msg); 43 base::PickleIterator iter(*msg);
44 return IPC::ParamTraits<CompositorFrame>::Read(msg, &iter, frame); 44 return IPC::ParamTraits<CompositorFrame>::Read(msg, &iter, frame);
45 } 45 }
46 46
47 static void RunDeserializationTestParamTraits(const std::string& test_name, 47 static void RunDeserializationTestParamTraits(
48 const CompositorFrame& frame) { 48 const std::string& test_name,
49 const CompositorFrame& frame,
50 UseSingleSharedQuadState single_sqs) {
49 IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); 51 IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
50 IPC::ParamTraits<CompositorFrame>::Write(&msg, frame); 52 IPC::ParamTraits<CompositorFrame>::Write(&msg, frame);
51 for (int i = 0; i < kNumWarmupRuns; ++i) { 53 for (int i = 0; i < kNumWarmupRuns; ++i) {
52 CompositorFrame compositor_frame; 54 CompositorFrame compositor_frame;
53 ReadMessage(&msg, &compositor_frame); 55 ReadMessage(&msg, &compositor_frame);
54 } 56 }
55 57
56 base::TimeTicks start = base::TimeTicks::Now(); 58 base::TimeTicks start = base::TimeTicks::Now();
57 base::TimeTicks end = 59 base::TimeTicks end =
58 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); 60 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis);
59 base::TimeTicks now = start; 61 base::TimeTicks now = start;
60 base::TimeDelta min_time; 62 base::TimeDelta min_time;
61 size_t count = 0; 63 size_t count = 0;
62 while (start < end) { 64 while (start < end) {
63 for (int i = 0; i < kTimeCheckInterval; ++i) { 65 for (int i = 0; i < kTimeCheckInterval; ++i) {
64 CompositorFrame compositor_frame; 66 CompositorFrame compositor_frame;
65 ReadMessage(&msg, &compositor_frame); 67 ReadMessage(&msg, &compositor_frame);
66 now = base::TimeTicks::Now(); 68 now = base::TimeTicks::Now();
67 // We don't count iterations after the end time. 69 // We don't count iterations after the end time.
68 if (now < end) 70 if (now < end)
69 ++count; 71 ++count;
70 } 72 }
71 73
72 if (now - start < min_time || min_time.is_zero()) 74 if (now - start < min_time || min_time.is_zero())
73 min_time = now - start; 75 min_time = now - start;
74 start = now; 76 start = now;
75 } 77 }
76 78
77 perf_test::PrintResult( 79 perf_test::PrintResult(
78 "ParamTraits deserialization: min_frame_deserialization_time", "", 80 "ParamTraits deserialization: min_frame_deserialization_time",
81 single_sqs == UseSingleSharedQuadState::YES
82 ? "_per_render_pass_shared_quad_state"
83 : "_per_quad_shared_quad_state",
79 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us", 84 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us",
80 true); 85 true);
81 perf_test::PrintResult("ParamTraits deserialization: num runs in 2 seconds", 86 perf_test::PrintResult("ParamTraits deserialization: num runs in 2 seconds",
82 "", test_name, count, "", true); 87 single_sqs == UseSingleSharedQuadState::YES
88 ? "_per_render_pass_shared_quad_state"
89 : "_per_quad_shared_quad_state",
90 test_name, count, "", true);
83 } 91 }
84 92
85 static void RunSerializationTestParamTraits(const std::string& test_name, 93 static void RunSerializationTestParamTraits(
86 const CompositorFrame& frame) { 94 const std::string& test_name,
95 const CompositorFrame& frame,
96 UseSingleSharedQuadState single_sqs) {
87 for (int i = 0; i < kNumWarmupRuns; ++i) { 97 for (int i = 0; i < kNumWarmupRuns; ++i) {
88 IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); 98 IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
89 IPC::ParamTraits<CompositorFrame>::Write(&msg, frame); 99 IPC::ParamTraits<CompositorFrame>::Write(&msg, frame);
90 } 100 }
91 101
92 base::TimeTicks start = base::TimeTicks::Now(); 102 base::TimeTicks start = base::TimeTicks::Now();
93 base::TimeTicks end = 103 base::TimeTicks end =
94 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); 104 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis);
95 base::TimeTicks now = start; 105 base::TimeTicks now = start;
96 base::TimeDelta min_time; 106 base::TimeDelta min_time;
97 size_t count = 0; 107 size_t count = 0;
98 while (start < end) { 108 while (start < end) {
99 for (int i = 0; i < kTimeCheckInterval; ++i) { 109 for (int i = 0; i < kTimeCheckInterval; ++i) {
100 IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); 110 IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
101 IPC::ParamTraits<CompositorFrame>::Write(&msg, frame); 111 IPC::ParamTraits<CompositorFrame>::Write(&msg, frame);
102 now = base::TimeTicks::Now(); 112 now = base::TimeTicks::Now();
103 // We don't count iterations after the end time. 113 // We don't count iterations after the end time.
104 if (now < end) 114 if (now < end)
105 ++count; 115 ++count;
106 } 116 }
107 117
108 if (now - start < min_time || min_time.is_zero()) 118 if (now - start < min_time || min_time.is_zero())
109 min_time = now - start; 119 min_time = now - start;
110 start = now; 120 start = now;
111 } 121 }
112 122
113 perf_test::PrintResult( 123 perf_test::PrintResult(
114 "ParamTraits serialization: min_frame_serialization_time", "", 124 "ParamTraits serialization: min_frame_serialization_time",
125 single_sqs == UseSingleSharedQuadState::YES
126 ? "_per_render_pass_shared_quad_state"
127 : "_per_quad_shared_quad_state",
115 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us", 128 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us",
116 true); 129 true);
117 perf_test::PrintResult("ParamTraits serialization: num runs in 2 seconds", 130 perf_test::PrintResult("ParamTraits serialization: num runs in 2 seconds",
118 "", test_name, count, "", true); 131 single_sqs == UseSingleSharedQuadState::YES
132 ? "_per_render_pass_shared_quad_state"
133 : "_per_quad_shared_quad_state",
134 test_name, count, "", true);
119 } 135 }
120 136
121 static void RunDeserializationTestStructTraits(const std::string& test_name, 137 static void RunDeserializationTestStructTraits(
122 const CompositorFrame& frame) { 138 const std::string& test_name,
139 const CompositorFrame& frame,
140 UseSingleSharedQuadState single_sqs) {
123 auto data = mojom::CompositorFrame::Serialize(&frame); 141 auto data = mojom::CompositorFrame::Serialize(&frame);
124 DCHECK_GT(data.size(), 0u); 142 DCHECK_GT(data.size(), 0u);
125 for (int i = 0; i < kNumWarmupRuns; ++i) { 143 for (int i = 0; i < kNumWarmupRuns; ++i) {
126 CompositorFrame compositor_frame; 144 CompositorFrame compositor_frame;
127 mojom::CompositorFrame::Deserialize(data, &compositor_frame); 145 mojom::CompositorFrame::Deserialize(data, &compositor_frame);
128 } 146 }
129 147
130 base::TimeTicks start = base::TimeTicks::Now(); 148 base::TimeTicks start = base::TimeTicks::Now();
131 base::TimeTicks end = 149 base::TimeTicks end =
132 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); 150 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis);
133 base::TimeTicks now = start; 151 base::TimeTicks now = start;
134 base::TimeDelta min_time; 152 base::TimeDelta min_time;
135 size_t count = 0; 153 size_t count = 0;
136 while (start < end) { 154 while (start < end) {
137 for (int i = 0; i < kTimeCheckInterval; ++i) { 155 for (int i = 0; i < kTimeCheckInterval; ++i) {
138 CompositorFrame compositor_frame; 156 CompositorFrame compositor_frame;
139 mojom::CompositorFrame::Deserialize(data, &compositor_frame); 157 mojom::CompositorFrame::Deserialize(data, &compositor_frame);
140 now = base::TimeTicks::Now(); 158 now = base::TimeTicks::Now();
141 // We don't count iterations after the end time. 159 // We don't count iterations after the end time.
142 if (now < end) 160 if (now < end)
143 ++count; 161 ++count;
144 } 162 }
145 163
146 if (now - start < min_time || min_time.is_zero()) 164 if (now - start < min_time || min_time.is_zero())
147 min_time = now - start; 165 min_time = now - start;
148 start = now; 166 start = now;
149 } 167 }
150 168
151 perf_test::PrintResult( 169 perf_test::PrintResult(
152 "StructTraits deserialization min_frame_deserialization_time", "", 170 "StructTraits deserialization min_frame_deserialization_time",
171 single_sqs == UseSingleSharedQuadState::YES
172 ? "_per_render_pass_shared_quad_state"
173 : "_per_quad_shared_quad_state",
153 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us", 174 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us",
154 true); 175 true);
155 perf_test::PrintResult( 176 perf_test::PrintResult(
156 "StructTraits deserialization: num runs in 2 seconds", "", test_name, 177 "StructTraits deserialization: num runs in 2 seconds",
157 count, "", true); 178 single_sqs == UseSingleSharedQuadState::YES
179 ? "_per_render_pass_shared_quad_state"
180 : "_per_quad_shared_quad_state",
181 test_name, count, "", true);
158 } 182 }
159 183
160 static void RunSerializationTestStructTraits(const std::string& test_name, 184 static void RunSerializationTestStructTraits(
161 const CompositorFrame& frame) { 185 const std::string& test_name,
186 const CompositorFrame& frame,
187 UseSingleSharedQuadState single_sqs) {
162 for (int i = 0; i < kNumWarmupRuns; ++i) { 188 for (int i = 0; i < kNumWarmupRuns; ++i) {
163 auto data = mojom::CompositorFrame::Serialize(&frame); 189 auto data = mojom::CompositorFrame::Serialize(&frame);
164 DCHECK_GT(data.size(), 0u); 190 DCHECK_GT(data.size(), 0u);
165 } 191 }
166 192
167 base::TimeTicks start = base::TimeTicks::Now(); 193 base::TimeTicks start = base::TimeTicks::Now();
168 base::TimeTicks end = 194 base::TimeTicks end =
169 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); 195 start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis);
170 base::TimeTicks now = start; 196 base::TimeTicks now = start;
171 base::TimeDelta min_time; 197 base::TimeDelta min_time;
172 size_t count = 0; 198 size_t count = 0;
173 while (start < end) { 199 while (start < end) {
174 for (int i = 0; i < kTimeCheckInterval; ++i) { 200 for (int i = 0; i < kTimeCheckInterval; ++i) {
175 auto data = mojom::CompositorFrame::Serialize(&frame); 201 auto data = mojom::CompositorFrame::Serialize(&frame);
176 DCHECK_GT(data.size(), 0u); 202 DCHECK_GT(data.size(), 0u);
177 now = base::TimeTicks::Now(); 203 now = base::TimeTicks::Now();
178 // We don't count iterations after the end time. 204 // We don't count iterations after the end time.
179 if (now < end) 205 if (now < end)
180 ++count; 206 ++count;
181 } 207 }
182 208
183 if (now - start < min_time || min_time.is_zero()) 209 if (now - start < min_time || min_time.is_zero())
184 min_time = now - start; 210 min_time = now - start;
185 start = now; 211 start = now;
186 } 212 }
187 213
188 perf_test::PrintResult( 214 perf_test::PrintResult(
189 "StructTraits serialization min_frame_serialization_time", "", 215 "StructTraits serialization min_frame_serialization_time",
216 single_sqs == UseSingleSharedQuadState::YES
217 ? "_per_render_pass_shared_quad_state"
218 : "_per_quad_shared_quad_state",
190 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us", 219 test_name, min_time.InMillisecondsF() / kTimeCheckInterval * 1000, "us",
191 true); 220 true);
192 perf_test::PrintResult("StructTraits serialization: num runs in 2 seconds", 221 perf_test::PrintResult("StructTraits serialization: num runs in 2 seconds",
193 "", test_name, count, "", true); 222 single_sqs == UseSingleSharedQuadState::YES
223 ? "_per_render_pass_shared_quad_state"
224 : "_per_quad_shared_quad_state",
225 test_name, count, "", true);
194 } 226 }
195 227
196 static void RunCompositorFrameTest(const std::string& test_name, 228 static void RunCompositorFrameTest(const std::string& test_name,
197 uint32_t num_quads, 229 uint32_t num_quads,
198 uint32_t num_passes, 230 uint32_t num_passes,
199 UseSingleSharedQuadState single_sqs) { 231 UseSingleSharedQuadState single_sqs) {
200 CompositorFrame frame; 232 CompositorFrame frame;
201 233
202 for (uint32_t i = 0; i < num_passes; ++i) { 234 for (uint32_t i = 0; i < num_passes; ++i) {
203 std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); 235 std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
204 render_pass->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform()); 236 render_pass->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
205 for (uint32_t j = 0; j < num_quads; ++j) { 237 for (uint32_t j = 0; j < num_quads; ++j) {
206 if (j == 0 || single_sqs == UseSingleSharedQuadState::NO) 238 if (j == 0 || single_sqs == UseSingleSharedQuadState::NO)
207 render_pass->CreateAndAppendSharedQuadState(); 239 render_pass->CreateAndAppendSharedQuadState();
208 const gfx::Rect bounds(100, 100, 100, 100); 240 const gfx::Rect bounds(100, 100, 100, 100);
209 const bool kForceAntiAliasingOff = true; 241 const bool kForceAntiAliasingOff = true;
210 SolidColorDrawQuad* quad = 242 SolidColorDrawQuad* quad =
211 render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); 243 render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
212 quad->SetNew(render_pass->shared_quad_state_list.back(), bounds, bounds, 244 quad->SetNew(render_pass->shared_quad_state_list.back(), bounds, bounds,
213 SK_ColorRED, kForceAntiAliasingOff); 245 SK_ColorRED, kForceAntiAliasingOff);
214 } 246 }
215 frame.render_pass_list.push_back(std::move(render_pass)); 247 frame.render_pass_list.push_back(std::move(render_pass));
216 } 248 }
217 RunTest(test_name, std::move(frame)); 249 RunTest(test_name, std::move(frame), single_sqs);
218 } 250 }
219 251
220 static void RunTest(const std::string& test_name, CompositorFrame frame) { 252 static void RunTest(const std::string& test_name,
221 RunSerializationTestStructTraits(test_name, frame); 253 CompositorFrame frame,
222 RunDeserializationTestStructTraits(test_name, frame); 254 UseSingleSharedQuadState single_sqs) {
223 RunSerializationTestParamTraits(test_name, frame); 255 RunSerializationTestStructTraits(test_name, frame, single_sqs);
224 RunDeserializationTestParamTraits(test_name, frame); 256 RunDeserializationTestStructTraits(test_name, frame, single_sqs);
257 RunSerializationTestParamTraits(test_name, frame, single_sqs);
258 RunDeserializationTestParamTraits(test_name, frame, single_sqs);
225 } 259 }
226 }; 260 };
227 261
262 // Test for compositor frames with one render pass and 4000 quads.
228 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_1_4000) { 263 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_1_4000) {
264 // Case 1: One shared quad state for all quads in one render pass.
229 RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_4000", 4000, 1, 265 RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_4000", 4000, 1,
230 UseSingleSharedQuadState::YES); 266 UseSingleSharedQuadState::YES);
267 // Case 2: One shared quad state for each quad.
268 RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_4000", 4000, 1,
269 UseSingleSharedQuadState::NO);
270 }
271
272 // Test for compositor frames with one render pass and 100000 quads.
273 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_1_100000) {
274 // Case 1: One shared quad state for all quads in one render pass.
275 RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_100000", 100000, 1,
276 UseSingleSharedQuadState::YES);
277 // Case 2: One shared quad state for each quad.
278 RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_100000", 100000, 1,
279 UseSingleSharedQuadState::NO);
280 }
281
282 // Test for compositor frames with 100 render pass and each with 4000 quads.
283 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_100_4000) {
284 // One shared quad state for all quads in one render pass.
285 RunCompositorFrameTest("DelegatedFrame_ManyQuads_100_4000", 4000, 100,
286 UseSingleSharedQuadState::YES);
231 } 287 }
232 288
233 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_1_100000) { 289 // Test for compositor frames with 10 render pass and each with 100000 quads.
234 RunCompositorFrameTest("DelegatedFrame_ManyQuads_1_100000", 100000, 1, 290 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_10_100000) {
291 // One shared quad state for all quads in one render pass.
292 RunCompositorFrameTest("DelegatedFrame_ManyQuads_10_100000", 100000, 10,
235 UseSingleSharedQuadState::YES); 293 UseSingleSharedQuadState::YES);
236 } 294 }
237 295
238 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_4000_4000) { 296 // Test for compositor frames with 5 render pass and each with 100 quads.
239 RunCompositorFrameTest("DelegatedFrame_ManyQuads_4000_4000", 4000, 1,
240 UseSingleSharedQuadState::NO);
241 }
242
243 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyQuads_100000_100000) {
244 RunCompositorFrameTest("DelegatedFrame_ManyQuads_100000_100000", 100000, 1,
245 UseSingleSharedQuadState::NO);
246 }
247
248 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_5_100) { 297 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_5_100) {
298 // Case 1: One shared quad state for all quads in one render pass.
299 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_5_100", 100, 5,
300 UseSingleSharedQuadState::YES);
301 // Case 2: One shared quad state for each quad.
249 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_5_100", 100, 5, 302 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_5_100", 100, 5,
250 UseSingleSharedQuadState::NO); 303 UseSingleSharedQuadState::NO);
251 } 304 }
252 305
306 // Test for compositor frames with 10 render pass and each with 500 quads.
253 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_10_500) { 307 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_10_500) {
308 // Case 1: One shared quad state for all quads in one render pass.
309 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10_500", 500, 10,
310 UseSingleSharedQuadState::YES);
311 // Case 2: One shared quad state for each quad.
254 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10_500", 500, 10, 312 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10_500", 500, 10,
255 UseSingleSharedQuadState::NO); 313 UseSingleSharedQuadState::NO);
256 } 314 }
257 315
316 // Test for compositor frames with 1000 render pass and each with 100 quads.
258 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_1000_100) { 317 TEST_F(CCSerializationPerfTest, DelegatedFrame_ManyRenderPasses_1000_100) {
259 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_10000_100", 100, 1000, 318 // Case 1: One shared quad state for all quads in one render pass.
319 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_1000_100", 100, 1000,
320 UseSingleSharedQuadState::YES);
321 // Case 2: One shared quad state for each quad.
322 RunCompositorFrameTest("DelegatedFrame_ManyRenderPasses_1000_100", 100, 1000,
260 UseSingleSharedQuadState::NO); 323 UseSingleSharedQuadState::NO);
261 } 324 }
262 325
263 } // namespace 326 } // namespace
264 } // namespace cc 327 } // namespace cc
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698