blob: 373f5b73d26ad45c92893562af4564b3d7686626 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include "runtime/query-exec-mgr.h"
#include "runtime/query-state.h"
#include "runtime/test-env.h"
#include "scheduling/request-pool-service.h"
#include "service/fe-support.h"
#include "util/benchmark.h"
#include "util/cpu-info.h"
#include "util/debug-util.h"
#include "util/metrics.h"
#include "util/pretty-printer.h"
#include "util/stopwatch.h"
#include "util/thread.h"
#include "util/uid-util.h"
#include "common/init.h"
#include "common/names.h"
/// This tests the performance of the following process wide locks:
//
/// 1. qs_map_lock_ (Sharded)
/// TODO: client_request_state_map_lock_ (Sharded)
//
/// A reasonable amount of queries are created and accessed multiple times via the
/// QueryExecMgr's APIs to benchmark the time taken to acquire the lock and retrieve
/// the QueryState.
//
/// ------------------Benchmark 1: Create and access Query States.
/// Total Time (#Queries: 5 #Accesses: 100) : 2202.44K clock cycles
/// Total Time (#Queries: 50 #Accesses: 100) : 4ms
/// Total Time (#Queries: 50 #Accesses: 1000) : 16ms
/// Total Time (#Queries: 500 #Accesses: 100) : 46ms
/// Total Time (#Queries: 500 #Accesses: 1000) : 129ms
/// Total Time (#Queries: 500 #Accesses: 5000) : 518ms
/// Total Time (#Queries: 1000 #Accesses: 1000) : 246ms
/// Total Time (#Queries: 1000 #Accesses: 5000) : 1s018ms
//
/// This was created to test improvements for IMPALA-4456.
using boost::uuids::random_generator;
using namespace impala;
boost::scoped_ptr<TestEnv> test_env_;
vector<TUniqueId> query_ids;
// This function creates a QueryState and accesses it 'num_accesses' times, via the
// QueryExecMgr APIs.
// TODO: Add a similar funciton for ClientRequestStates.
void CreateAndAccessQueryStates(const TUniqueId& query_id, int num_accesses) {
TQueryCtx query_ctx;
query_ctx.query_id = query_id;
string resolved_pool;
Status s = ExecEnv::GetInstance()->request_pool_service()->ResolveRequestPool(
query_ctx, &resolved_pool);
query_ctx.__set_request_pool(resolved_pool);
QueryState *query_state;
query_state = ExecEnv::GetInstance()->query_exec_mgr()->CreateQueryState(query_ctx, -1);
DCHECK(query_state != nullptr);
query_state->AcquireBackendResourceRefcount();
for (int i=0; i < num_accesses ; ++i) {
QueryState* qs;
qs = ExecEnv::GetInstance()->query_exec_mgr()->GetQueryState(query_id);
DCHECK(qs != nullptr);
ExecEnv::GetInstance()->query_exec_mgr()->ReleaseQueryState(qs);
}
query_state->ReleaseBackendResourceRefcount();
// This should drop the last reference count to the QueryState and destroy it.
ExecEnv::GetInstance()->query_exec_mgr()->ReleaseQueryState(query_state);
// Make sure that the query doesn't exist in the map any longer.
DCHECK(ExecEnv::GetInstance()->query_exec_mgr()->GetQueryState(query_id) == nullptr);
}
// Runs 'num_threads' Impala Threads and have each of them execute func().
void ImpalaThreadStarter(void (*func) (const TUniqueId&, int), int num_threads,
int func_arg) {
vector<unique_ptr<Thread>> threads;
threads.reserve(num_threads);
for (int i=0; i < num_threads; ++i) {
unique_ptr<Thread> thread;
function<void ()> f =
bind(func, query_ids[i], func_arg);
Status s =
Thread::Create("mythreadgroup", "thread", f, &thread);
DCHECK(s.ok());
threads.push_back(move(thread));
}
for (unique_ptr<Thread>& thread: threads) {
thread->Join();
}
}
void RunBenchmark(int num_queries, int num_accesses) {
StopWatch total_time;
total_time.Start();
ImpalaThreadStarter(CreateAndAccessQueryStates, num_queries, num_accesses);
total_time.Stop();
cout << "Total Time " << "(#Queries: " << num_queries << " #Accesses: "
<< num_accesses << ") : "
<< PrettyPrinter::Print(total_time.ElapsedTime(), TUnit::CPU_TICKS) << endl;
}
// Create and store 'num_queries' Query IDs into 'query_ids'.
void CreateQueryIds(int num_queries) {
for (int i=0; i < num_queries; ++i) {
query_ids[i] = UuidToQueryId(random_generator()());
}
}
int main(int argc, char **argv) {
// Though we don't use the JVM or require FeSupport, the TestEnv class requires it,
// so we start them up.
impala::InitCommonRuntime(argc, argv, true, impala::TestInfo::BE_TEST);
impala::InitFeSupport();
const int MAX_QUERIES = 1000;
query_ids.reserve(MAX_QUERIES);
test_env_.reset(new TestEnv());
ABORT_IF_ERROR(test_env_->Init());
CreateQueryIds(MAX_QUERIES);
cout << "------------------Benchmark 1: Create and access Query States." << endl;
RunBenchmark(5, 100);
RunBenchmark(50, 100);
RunBenchmark(50, 1000);
RunBenchmark(500, 100);
RunBenchmark(500, 1000);
RunBenchmark(500, 5000);
RunBenchmark(1000, 1000);
RunBenchmark(1000, 5000);
cout << endl;
// TODO: Benchmark lock of ClientRequestStates too.
return 0;
}