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

Side by Side Diff: net/websockets/websocket_frame_unittest.cc

Issue 11572010: Optimise MaskWebSocketFramePayload(). (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years 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
OLDNEW
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
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
OLDNEW
« net/websockets/websocket_frame.cc ('K') | « net/websockets/websocket_frame.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698