llvm-mirror/unittests/Support/raw_sha1_ostream_test.cpp
Nick Terrell 639a0c16d7 [Support] Optimize SHA1 implementation
* Add inline to the helper functions because gcc-9 won't inline all of
  them without the hint. I've avoided `__attribute__((always_inline))`
  because gcc and clang will inline without it, and improves
  compatibility.
* Replace the byte-by-byte copy in update() with endian::readbe32()
  since perf reports that 1/2 of the time is spent copying into the
  buffer before this patch.

When lld uses --build-id=sha1 it spends 30-45% of CPU in SHA1 depending on the binary (not wall-time since it is parallel). This patch speeds up SHA1 by a factor of 2 on clang-8 and 3 on gcc-6. This leads to a >10% improvement in overall linking time.

lld-speed-test benchmarks run on an Intel i9-9900k with Turbo disabled on CPU 0 compiled with clang-9. Stats recorded with `perf stat -r 5`. All inputs are using `--build-id=sha1`.

| Input | Before (seconds) | After (seconds) |
| --- | --- | --- |
| chrome | 2.14 | 1.82 (-15%) |
| chrome-icf | 2.56 | 2.29 (-10%) |
| clang | 0.65 | 0.53 (-18%) |
| clang-fsds | 0.69 | 0.58 (-16%) |
| clang-gdb-index | 21.71 | 19.3 (-11%) |
| gold | 0.42 | 0.34 (-19%) |
| gold-fsds | 0.431 | 0.355 (-17%) |
| linux-kernel | 0.625 | 0.575 (-8%) |
| llvm-as | 0.045 | 0.039 (-14%) |
| llvm-as-fsds | 0.035 | 0.039 (-11%) |
| mozilla | 11.3 | 9.8  (-13%) |
| mozilla-gc | 11.84 | 10.36 (-12%) |
| mozilla-O0 | 8.2 | 5.84 (-28%) |
| scylla | 5.59 | 4.52 (-19%) |

Reviewed By: ruiu, MaskRay

Differential Revision: https://reviews.llvm.org/D69295
2019-11-11 22:14:28 -08:00

94 lines
2.7 KiB
C++

//===- llvm/unittest/Support/raw_ostream_test.cpp - raw_ostream tests -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_sha1_ostream.h"
#include "gtest/gtest.h"
#include <string>
using namespace llvm;
static std::string toHex(StringRef Input) {
static const char *const LUT = "0123456789ABCDEF";
size_t Length = Input.size();
std::string Output;
Output.reserve(2 * Length);
for (size_t i = 0; i < Length; ++i) {
const unsigned char c = Input[i];
Output.push_back(LUT[c >> 4]);
Output.push_back(LUT[c & 15]);
}
return Output;
}
TEST(raw_sha1_ostreamTest, Basic) {
llvm::raw_sha1_ostream Sha1Stream;
Sha1Stream << "Hello World!";
auto Hash = toHex(Sha1Stream.sha1());
ASSERT_EQ("2EF7BDE608CE5404E97D5F042F95F89F1C232871", Hash);
}
TEST(sha1_hash_test, Basic) {
ArrayRef<uint8_t> Input((const uint8_t *)"Hello World!", 12);
std::array<uint8_t, 20> Vec = SHA1::hash(Input);
std::string Hash = toHex({(const char *)Vec.data(), 20});
ASSERT_EQ("2EF7BDE608CE5404E97D5F042F95F89F1C232871", Hash);
}
TEST(sha1_hash_test, Update) {
SHA1 sha1;
std::string Input = "123456789012345678901234567890";
ASSERT_EQ(Input.size(), 30UL);
// 3 short updates.
sha1.update(Input);
sha1.update(Input);
sha1.update(Input);
// Long update that gets into the optimized loop with prefix/suffix.
sha1.update(Input + Input + Input + Input);
// 18 bytes buffered now.
std::string Hash = toHex(sha1.final());
ASSERT_EQ("3E4A614101AD84985AB0FE54DC12A6D71551E5AE", Hash);
}
// Check that getting the intermediate hash in the middle of the stream does
// not invalidate the final result.
TEST(raw_sha1_ostreamTest, Intermediate) {
llvm::raw_sha1_ostream Sha1Stream;
Sha1Stream << "Hello";
auto Hash = toHex(Sha1Stream.sha1());
ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash);
Sha1Stream << " World!";
Hash = toHex(Sha1Stream.sha1());
// Compute the non-split hash separately as a reference.
llvm::raw_sha1_ostream NonSplitSha1Stream;
NonSplitSha1Stream << "Hello World!";
auto NonSplitHash = toHex(NonSplitSha1Stream.sha1());
ASSERT_EQ(NonSplitHash, Hash);
}
TEST(raw_sha1_ostreamTest, Reset) {
llvm::raw_sha1_ostream Sha1Stream;
Sha1Stream << "Hello";
auto Hash = toHex(Sha1Stream.sha1());
ASSERT_EQ("F7FF9E8B7BB2E09B70935A5D785E0CC5D9D0ABF0", Hash);
Sha1Stream.resetHash();
Sha1Stream << " World!";
Hash = toHex(Sha1Stream.sha1());
ASSERT_EQ("7447F2A5A42185C8CF91E632789C431830B59067", Hash);
}