OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "net/websockets/websocket_frame.h" | 5 #include "net/websockets/websocket_frame.h" |
6 | 6 |
7 #include <algorithm> | |
7 #include <vector> | 8 #include <vector> |
8 | 9 |
9 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
11 #include "base/command_line.h" | |
12 #include "base/memory/aligned_memory.h" | |
13 #include "base/string_number_conversions.h" | |
14 #include "base/time.h" | |
10 #include "net/base/net_errors.h" | 15 #include "net/base/net_errors.h" |
11 #include "testing/gtest/include/gtest/gtest.h" | 16 #include "testing/gtest/include/gtest/gtest.h" |
12 | 17 |
18 // Run | |
19 // out/Release/net_unittests --websocket-mask-iterations=100000 | |
20 // --gtest_filter='WebSocketFrameTestMaskBenchmark.*' | |
21 // to benchmark the MaskWebSocketFramePayload() function. | |
22 static const char kBenchmarkIterations[] = "websocket-mask-iterations"; | |
23 static const int kDefaultIterations = 10; | |
24 static const int kLongPayloadSize = 1<<16; | |
Takashi Toyoshima
2012/12/14 07:04:11
[optional] I feel that usually we place spaces bef
Adam Rice
2012/12/14 08:05:37
Done.
| |
25 | |
13 namespace net { | 26 namespace net { |
14 | 27 |
15 TEST(WebSocketFrameHeaderTest, FrameLengths) { | 28 TEST(WebSocketFrameHeaderTest, FrameLengths) { |
16 struct TestCase { | 29 struct TestCase { |
17 const char* frame_header; | 30 const char* frame_header; |
18 size_t frame_header_length; | 31 size_t frame_header_length; |
19 uint64 frame_length; | 32 uint64 frame_length; |
20 }; | 33 }; |
21 static const TestCase kTests[] = { | 34 static const TestCase kTests[] = { |
22 { "\x81\x00", 2, GG_UINT64_C(0) }, | 35 { "\x81\x00", 2, GG_UINT64_C(0) }, |
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
260 std::vector<char> expected_output(kTests[i].output, | 273 std::vector<char> expected_output(kTests[i].output, |
261 kTests[i].output + kTests[i].data_length); | 274 kTests[i].output + kTests[i].data_length); |
262 MaskWebSocketFramePayload(masking_key, | 275 MaskWebSocketFramePayload(masking_key, |
263 kTests[i].frame_offset, | 276 kTests[i].frame_offset, |
264 frame_data.empty() ? NULL : &frame_data.front(), | 277 frame_data.empty() ? NULL : &frame_data.front(), |
265 frame_data.size()); | 278 frame_data.size()); |
266 EXPECT_EQ(expected_output, frame_data); | 279 EXPECT_EQ(expected_output, frame_data); |
267 } | 280 } |
268 } | 281 } |
269 | 282 |
283 // Check that all combinations of alignment, frame offset and chunk size work | |
284 // correctly for MaskWebSocketFramePayload(). This is mainly used to ensure that | |
285 // vectorisation optimisations don't break anything. We could take a "white box" | |
286 // approach and only test the edge cases, but since the exhaustive "black box" | |
287 // approach runs in acceptable time, we don't have to take the risk of being | |
288 // clever. | |
289 // | |
290 // This brute-force approach runs in O(N^3) time where N is the size of the | |
291 // maximum vector size we want to test again. This might need reconsidering if | |
292 // MaskWebSocketFramePayload() is ever optimised for a dedicated vector | |
293 // architecture. | |
294 TEST(WebSocketFrameTest, MaskPayloadAlignment) { | |
295 // This reflects what might be implemented in the future, rather than | |
296 // the current implementation. FMA3 and FMA4 support 256-bit vector ops. | |
297 static const size_t kMaxVectorSizeInBits = 256; | |
298 static const size_t kMaxVectorSize = kMaxVectorSizeInBits / 8; | |
299 static const size_t kMaxVectorAlignment = kMaxVectorSize; | |
300 static const size_t kMaskingKeyLength = | |
301 WebSocketFrameHeader::kMaskingKeyLength; | |
302 static const size_t kScratchBufferSize = | |
303 kMaxVectorAlignment + kMaxVectorSize * 2; | |
304 static const char kTestMask[kMaskingKeyLength] = { 0xd2, 0xba, 0x5a, 0xbe }; | |
305 // We use 786 bits of random input to reduce the risk of correlated errors. | |
306 static const char kTestInput[] = | |
307 { 0x3d, 0x77, 0x1d, 0x1b, 0x19, 0x8c, 0x48, 0xa3, 0x19, 0x6d, 0xf7, 0xcc, | |
308 0x39, 0xe7, 0x57, 0x0b, 0x69, 0x8c, 0xda, 0x4b, 0xfc, 0xac, 0x2c, 0xd3, | |
309 0x49, 0x96, 0x6e, 0x8a, 0x7b, 0x5a, 0x32, 0x76, 0xd0, 0x11, 0x43, 0xa0, | |
310 0x89, 0xfc, 0x76, 0x2b, 0x10, 0x2f, 0x4c, 0x7b, 0x4f, 0xa6, 0xdd, 0xe4, | |
311 0xfc, 0x8e, 0xd8, 0x72, 0xcf, 0x7e, 0x37, 0xcd, 0x31, 0xcd, 0xc1, 0xc0, | |
312 0x89, 0x0c, 0xa7, 0x4c, 0xda, 0xa8, 0x4b, 0x75, 0xa1, 0xcb, 0xa9, 0x77, | |
313 0x19, 0x4d, 0x6e, 0xdf, 0xc8, 0x08, 0x1c, 0xb6, 0x6d, 0xfb, 0x38, 0x04, | |
314 0x44, 0xd5, 0xba, 0x57, 0x9f, 0x76, 0xb0, 0x2e, 0x07, 0x91, 0xe6, 0xa8 | |
315 }; | |
316 static const char kTestOutput[] = | |
317 { 0xef, 0xcd, 0x47, 0xa5, 0xcb, 0x36, 0x12, 0x1d, 0xcb, 0xd7, 0xad, 0x72, | |
318 0xeb, 0x5d, 0x0d, 0xb5, 0xbb, 0x36, 0x80, 0xf5, 0x2e, 0x16, 0x76, 0x6d, | |
319 0x9b, 0x2c, 0x34, 0x34, 0xa9, 0xe0, 0x68, 0xc8, 0x02, 0xab, 0x19, 0x1e, | |
320 0x5b, 0x46, 0x2c, 0x95, 0xc2, 0x95, 0x16, 0xc5, 0x9d, 0x1c, 0x87, 0x5a, | |
321 0x2e, 0x34, 0x82, 0xcc, 0x1d, 0xc4, 0x6d, 0x73, 0xe3, 0x77, 0x9b, 0x7e, | |
322 0x5b, 0xb6, 0xfd, 0xf2, 0x08, 0x12, 0x11, 0xcb, 0x73, 0x71, 0xf3, 0xc9, | |
323 0xcb, 0xf7, 0x34, 0x61, 0x1a, 0xb2, 0x46, 0x08, 0xbf, 0x41, 0x62, 0xba, | |
324 0x96, 0x6f, 0xe0, 0xe9, 0x4d, 0xcc, 0xea, 0x90, 0xd5, 0x2b, 0xbc, 0x16 | |
325 }; | |
326 scoped_ptr_malloc<char, base::ScopedPtrAlignedFree> scratch( | |
327 static_cast<char*>(base::AlignedAlloc(kScratchBufferSize, | |
328 kMaxVectorAlignment))); | |
329 WebSocketMaskingKey masking_key; | |
330 std::copy(kTestMask, kTestMask + kMaskingKeyLength, masking_key.key); | |
331 for (size_t frame_offset = 0; | |
332 frame_offset < kMaskingKeyLength; | |
333 ++frame_offset) { | |
334 for (size_t alignment = 0; alignment < kMaxVectorAlignment; ++alignment) { | |
335 char* const aligned_scratch = scratch.get() + alignment; | |
336 const size_t aligned_len = | |
337 std::min(kScratchBufferSize - alignment, | |
338 arraysize(kTestInput) - frame_offset); | |
339 for (size_t chunk_size = 1; chunk_size < kMaxVectorSize; ++chunk_size) { | |
340 memcpy(aligned_scratch, kTestInput + frame_offset, aligned_len); | |
341 for (size_t chunk_start = 0; | |
342 chunk_start < aligned_len; | |
343 chunk_start += chunk_size) { | |
344 const size_t this_chunk_size = std::min(chunk_size, | |
345 aligned_len - chunk_start); | |
346 MaskWebSocketFramePayload(masking_key, | |
347 frame_offset + chunk_start, | |
348 aligned_scratch + chunk_start, | |
349 this_chunk_size); | |
350 } | |
351 // Stop the test if it fails, since we don't want to spew thousands of | |
352 // failures. | |
353 ASSERT_TRUE(std::equal(aligned_scratch, aligned_scratch + aligned_len, | |
354 kTestOutput + frame_offset)) | |
355 << "Output failed to match for frame_offset=" | |
356 << frame_offset | |
357 << ", alignment=" | |
358 << alignment | |
359 << ", chunk_size=" | |
360 << chunk_size; | |
361 } | |
362 } | |
363 } | |
364 } | |
365 | |
366 class WebSocketFrameTestMaskBenchmark : public testing::Test { | |
367 public: | |
368 WebSocketFrameTestMaskBenchmark() | |
369 : iterations_(kDefaultIterations) {} | |
370 | |
371 void SetUp() { | |
372 std::string iterations( | |
373 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
374 kBenchmarkIterations)); | |
375 int benchmark_iterations = 0; | |
376 if (!iterations.empty() && base::StringToInt(iterations, | |
377 &benchmark_iterations)) { | |
378 iterations_ = benchmark_iterations; | |
379 } | |
380 } | |
381 | |
382 void Benchmark(const char* const payload, size_t size) { | |
383 std::vector<char> scratch(payload, payload + size); | |
384 static const char kMaskingKey[] = "\xFE\xED\xBE\xEF"; | |
385 COMPILE_ASSERT(arraysize(kMaskingKey) == | |
386 WebSocketFrameHeader::kMaskingKeyLength + 1, | |
387 incorrect_masking_key_size); | |
388 WebSocketMaskingKey masking_key; | |
389 std::copy(kMaskingKey, kMaskingKey + | |
390 WebSocketFrameHeader::kMaskingKeyLength, | |
391 masking_key.key); | |
392 printf("Benchmarking MaskWebSocketFramePayload() for %d iterations\n", | |
Takashi Toyoshima
2012/12/14 07:04:11
Usually we use LOG or DLOG for this kind of messag
Adam Rice
2012/12/14 08:05:37
I chose LOG() since the benchmark is only useful w
| |
393 iterations_); | |
394 using base::TimeTicks; | |
395 TimeTicks start = TimeTicks::HighResNow(); | |
396 for (int x = 0; x < iterations_; ++x) { | |
397 MaskWebSocketFramePayload(masking_key, x % size, &scratch.front(), | |
398 scratch.size()); | |
399 } | |
400 double total_time_ms = | |
401 1000 * (TimeTicks::HighResNow() - start).InMillisecondsF() / | |
402 iterations_; | |
403 printf("Payload size %d took %.03f microseconds per iteration\n", | |
Takashi Toyoshima
2012/12/14 07:04:11
ditto.
Adam Rice
2012/12/14 08:05:37
Done.
| |
404 static_cast<int>(size), total_time_ms); | |
405 } | |
406 | |
407 private: | |
408 int iterations_; | |
409 | |
410 DISALLOW_COPY_AND_ASSIGN(WebSocketFrameTestMaskBenchmark); | |
411 }; | |
412 | |
413 TEST_F(WebSocketFrameTestMaskBenchmark, BenchmarkMaskShortPayload) { | |
414 static const char kShortPayload[] = "Short Payload"; | |
415 Benchmark(kShortPayload, arraysize(kShortPayload)); | |
416 } | |
417 | |
418 TEST_F(WebSocketFrameTestMaskBenchmark, BenchmarkMaskLongPayload) { | |
419 scoped_array<char> payload(new char[kLongPayloadSize]); | |
420 std::fill(payload.get(), payload.get() + kLongPayloadSize, 'a'); | |
421 Benchmark(payload.get(), kLongPayloadSize); | |
422 } | |
423 | |
270 } // namespace net | 424 } // namespace net |
OLD | NEW |