Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
growing-test.cc
Go to the documentation of this file.
1 #include <gtest/gtest.h>
2 
3 #include "hash-store.h"
4 #include "standard-hash-table.h"
5 #include "tuple-types.h"
6 #include "util/cpu-info.h"
7 #include "util/debug-util.h"
8 #include "util/pretty-printer.h"
9 #include "util/stopwatch.h"
10 
11 using namespace impala;
12 
13 namespace impala {
14 
15 // Tests performance of hash aggregation with HashStore, which grows to contain multiple
16 // hash tables as needed. Allows exploring how buffer size and size of the keyspace
17 // affect performance.
18 class GrowingTest : public testing::Test {
19  public:
20  static const uint64_t NUM_PROBE_TUPLES = 100000000; // 10^8
21 
22  template <int buffer_size>
23  inline static uint64_t AggregateTest(uint64_t num_probe_tuples, int max_id,
24  int num_tables) {
25  ProbeTuple* tuples = GenTuples(num_probe_tuples, max_id);
26  HashStore<buffer_size> hs;
27  StopWatch watch;
28  watch.Start();
29  hs.Aggregate(tuples, num_probe_tuples);
30  watch.Stop();
31  SanityCheck<buffer_size>(hs, num_probe_tuples, max_id, num_tables);
32  free(tuples);
33  return watch.ElapsedTime();
34  }
35 
36  // Confirm that hs appears to be the correct result of aggregating num_probe_tuples,
37  // with a keyspace of size max_id, with a fanout into num_tables tables.
38  template <int buffer_size>
39  static void SanityCheck(const HashStore<buffer_size> & hs,
40  uint64_t num_probe_tuples, int max_id, int num_tables) {
41  ASSERT_EQ(num_probe_tuples, hs.TupleCount());
42  // Because CRC hashes contiguous integers nearly perfectly, we should always have
43  // exactly num_tables tables, even though we grow on demand.
44  ASSERT_EQ(num_tables, hs.TableCount())
45  << "You do not have the number of tables that you hoped.\n"
46  << "This could mean that there weren't enough probe tuples to fill the keyspace, "
47  << "skewed hashing lead a hash table that we expected not to overflow to overflow, "
48  << "or a genuine bug.";
49 #ifdef NDEBUG
50  if (buffer_size > 0) {
51  // Can't check this in debug mode because it won't neccessarily pack the struct.
52  ASSERT_EQ(buffer_size, sizeof(typename HashStore<buffer_size>::Buffer));
53  }
54 #endif
55  }
56 
57  template <int buffer_size>
58  inline static uint64_t NextTestCase(int build_tuples, uint64_t prev, int num_tables) {
59  uint64_t time = AggregateTest<buffer_size>(NUM_PROBE_TUPLES, build_tuples, num_tables);
60  int64_t delta;
61  if (prev == 0) {
62  // just print 0 for the delta the first time.
63  delta = 0;
64  }
65  else {
66  delta = static_cast<int64_t>(time) - static_cast<int64_t>(prev);
67  }
68  LOG(ERROR) << build_tuples << "\t"
69  << PrettyPrinter::Print(time, TUnit::CPU_TICKS)
70  << "\t" << PrettyPrinter::Print(delta, TUnit::CPU_TICKS);
71  return time;
72  }
73 
74  // Run a test aggregation with buffers of size buffer_size bytes.
75  // Try the full range of working set size (and thus fanout) that we can do with
76  // single-level fanout.
77  template <int buffer_size>
78  inline static void Test() {
79  LOG(ERROR) << "Buffer size " << HashStore<buffer_size>::BUFFER_SIZE << " tuples ("
80  << PrettyPrinter::Print(buffer_size, TUnit::BYTES)
81  << "):";
82  LOG(ERROR) << "#BuildTuples\tTime\tdTime";
83  uint64_t prev = 0;
84  for (int num_tables = 1; num_tables <= 1<<16; num_tables *= 2) {
85  // how many total build tuples we'll use to fill num_tables tables
86  // Needs to be comfortably less than the total capacity of num_tables tables
87  // (the below expression without the constant factor multiplied by it)
88  // because the hashes will not be spread perfectly.
89  // But should be more than 1/2 of that expression because otherwise if the hashes
90  // spread out perfectly, it will fit in num_tables / 2 and not give us the fanout
91  // we expect. (A test will fail in this case so that we know.)
92  int build_tuples = (StandardHashTable::NODES * num_tables / 10) * 8;
93  prev = NextTestCase<buffer_size>(build_tuples, prev, num_tables);
94  }
95  }
96 };
97 
98 }
99 
100 int main(int argc, char** argv) {
101  google::InitGoogleLogging(argv[0]);
102  ::testing::InitGoogleTest(&argc, argv);
103  CpuInfo::Init();
104  LOG(ERROR) << "Testing with " << GrowingTest::NUM_PROBE_TUPLES << " tuples.";
105  return RUN_ALL_TESTS();
106 }
107 
109  // test with one of the best buffer sizes
110  // Make it slightly more than a power of 2 cache lines so that they are offset.
111  GrowingTest::Test< (1 << 13) + 3 * 64 >();
112 }
uint64_t ElapsedTime() const
Returns time in cpu ticks.
Definition: stopwatch.h:50
static void Test()
Definition: growing-test.cc:78
static void SanityCheck(const HashStore< buffer_size > &hs, uint64_t num_probe_tuples, int max_id, int num_tables)
Definition: growing-test.cc:39
ProbeTuple tuples[BUFFER_TUPLES]
TEST(AtomicTest, Basic)
Definition: atomic-test.cc:28
#define BUFFER_SIZE
static std::string Print(bool value, TUnit::type ignored, bool verbose=false)
static uint64_t NextTestCase(int build_tuples, uint64_t prev, int num_tables)
Definition: growing-test.cc:58
static const uint64_t NUM_PROBE_TUPLES
Definition: growing-test.cc:20
uint64_t Test(T *ht, const ProbeTuple *input, uint64_t num_tuples)
int main(int argc, char **argv)
static void Init()
Initialize CpuInfo.
Definition: cpu-info.cc:75
static uint64_t AggregateTest(uint64_t num_probe_tuples, int max_id, int num_tables)
Definition: growing-test.cc:23