blob: 21cd4beda1f4c625c8f20e9574e5307a36a0e200 [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.
*/
/**
* @author Yuri Kashnikov
*
* The idea of advanced Top-N-Value (with steady and clear parts, and clear
* interval) from <"Value profiling and optimization", B.Calder, P.Feller,
* Journal of Instruction-Level Parallelism, 1999>
*
*/
#define LOG_DOMAIN "em"
#include "cxxlog.h"
#include "NValueProfileCollector.h"
#include <algorithm>
#include <sstream>
#include "open/vm_class_info.h"
#include "open/vm_ee.h"
#include "port_threadunsafe.h"
#include "port_atomic.h"
#include "open/vm_class_manipulation.h"
#include "open/vm.h" // for vtable_get_class
VPInstructionProfileData* TNVTableManager::createProfileData()
{
VPInstructionProfileData* data = new VPInstructionProfileData();
data->TNV_Table = new (struct Simple_TNV_Table[steadySize]);
for (U_32 i = 0; i < steadySize; i++) {
(data->TNV_Table[i]).frequency = 0;
(data->TNV_Table[i]).value = 0;
}
if (clearSize > 0) {
data->TNV_clear_part = new (struct Simple_TNV_Table[clearSize]);
for (U_32 i = 0; i < clearSize; i++) {
(data->TNV_clear_part[i]).frequency = 0;
(data->TNV_clear_part[i]).value = 0;
}
}
return data;
}
I_32 TNVTableManager::find(TableT* where, ValueT value_to_search, U_32 size)
{
U_32 search_index;
for (search_index = 0; search_index < size; search_index++){
if (where[search_index].value == value_to_search)
return (search_index);
}
return (-1);
}
void TNVTableManager::clearTopElements(TableT* where)
{
U_32 temp_index;
for (temp_index = 0; temp_index < clearSize; temp_index++) {
where[temp_index].frequency = TNV_DEFAULT_CLEAR_VALUE;
}
}
I_32 TNVTableManager::findMinIdx(TableT* where, U_32 size)
{
U_32 temp_index;
U_32 temp_min_index = 0;
U_32 temp_min = where[temp_min_index].frequency;
for (temp_index = 0; temp_index < size; temp_index++){
if (where[temp_index].frequency == TNV_DEFAULT_CLEAR_VALUE) {
return (temp_index);
}
if (where[temp_index].frequency < temp_min){
temp_min = where[temp_index].frequency;
temp_min_index = temp_index;
}
}
return (temp_min_index);
}
TNVTableManager::ValueT TNVTableManager::findMax(TableT *where)
{
ValueT max_value = 0;
U_32 temp_index, temp_max_frequency = 0;
for (temp_index = 0; temp_index < steadySize; temp_index++) {
TableT *current_tbl = &(where[temp_index]);
if (current_tbl->frequency > temp_max_frequency){
temp_max_frequency = current_tbl->frequency;
max_value = current_tbl->value;
}
}
return (max_value);
}
void TNVTableManager::flushLastValueCounter(VPData *instProfile)
{
POINTER_SIZE_INT last_value = instProfile->last_value;
U_32* num_times_profiled = &(instProfile->num_times_profiled);
struct Simple_TNV_Table* clear_part = instProfile->TNV_clear_part;
struct Simple_TNV_Table* steady_part = instProfile->TNV_Table;
insert(steady_part, clear_part, last_value, *num_times_profiled);
*num_times_profiled = 0;
}
void TNVTableManager::dumpValues
(VPInstructionProfileData* data, std::ostream& os)
{
os << ", num_times_profiled: " << data->num_times_profiled
<< ", profile_tick: " << data->profile_tick << std::endl;
struct Simple_TNV_Table * TNV_steady_part = data->TNV_Table;
if (TNV_steady_part != NULL) {
U_32 size = steadySize;
os << "= TNV_steady_part, size = " << size << std::endl;
for (U_32 i = 0; i < size; i++) {
os << "== Frequency: " << TNV_steady_part[i].frequency << " = Value: ";
POINTER_SIZE_INT value = TNV_steady_part[i].value;
if (value != 0) {
os << class_get_name(vtable_get_class((VTable_Handle)value));
} else {
os << "NULL";
}
os << " ==" << std::endl;
}
}
struct Simple_TNV_Table * TNV_clear_part = data->TNV_clear_part;
if (TNV_clear_part != NULL) {
U_32 size = clearSize;
os << "= TNV_clear_part, size = " << size << std::endl;
for (U_32 i = 0; i < size; i++) {
os << "== " << TNV_clear_part[i].frequency << " = Value: ";
POINTER_SIZE_INT value = TNV_clear_part[i].value;
if (value != 0) {
os << class_get_name(vtable_get_class((VTable_Handle)value));
} else {
os << "NULL";
}
os << " ==" << std::endl;
}
}
}
//------------------------------------------------------------------------------
void TNVTableFirstNManager::insert(TableT* where, TableT* clear_part,
ValueT value_to_insert, U_32 times_met)
{
U_32 insert_index = find(where, value_to_insert, steadySize);
if ((insert_index != -1) &&
(where[insert_index].frequency != TNV_DEFAULT_CLEAR_VALUE)){
where[insert_index].frequency += times_met;
} else {
insert_index = findMinIdx(where, steadySize);
if (times_met > where[insert_index].frequency){
where[insert_index].value = value_to_insert;
where[insert_index].frequency = times_met;
}
}
}
void TNVTableFirstNManager::addNewValue(ValueMethodProfile* methProfile,
VPData* instProfile, ValueT curr_value)
{
U_8* updating_ptr = methProfile->getUpdatingStatePtr();
if (updateStrategy == UPDATE_LOCKED) {
methProfile->lockProfile();
} else if (updateStrategy == UPDATE_FLAGGED_ALL) {
// Checking a flag and modifying it atomically must be faster than
// locking because it skips simultaneous updates. Faster but sacrifices
// profile precision.
if (port_atomic_cas8(updating_ptr, 1, 0) != 0) {
return;
}
}
UNSAFE_REGION_START
ValueT* last_value = &(instProfile->last_value);
U_32* num_times_profiled = &(instProfile->num_times_profiled);
if (curr_value == *last_value){
// We increment the counter safely with UPDATE_FLAGGED_ALL and
// UPDATE_LOCKED
(*num_times_profiled)++;
} else {
if (updateStrategy == UPDATE_LOCKED_INSERT) {
methProfile->lockProfile();
}else if (updateStrategy == UPDATE_FLAGGED_INSERT) {
if (port_atomic_cas8(updating_ptr, 1, 0) != 0) {
return;
}
}
struct Simple_TNV_Table* clear_part = instProfile->TNV_clear_part;
struct Simple_TNV_Table* steady_part = instProfile->TNV_Table;
flushLastValueCounter(instProfile);
*num_times_profiled = 1;
insert(steady_part, clear_part, curr_value, *num_times_profiled);
*last_value = curr_value;
if (updateStrategy == UPDATE_LOCKED_INSERT) {
methProfile->unlockProfile();
}else if (updateStrategy == UPDATE_FLAGGED_INSERT) {
*updating_ptr = 0;
}
}
UNSAFE_REGION_END
if (updateStrategy == UPDATE_LOCKED) {
methProfile->unlockProfile();
} else if (updateStrategy == UPDATE_FLAGGED_ALL) {
*updating_ptr = 0;
}
}
//------------------------------------------------------------------------------
void TNVTableDividedManager::insert(TableT* where, TableT* clear_part,
ValueT value_to_insert, U_32 times_met)
{
U_32 insert_index = find(where, value_to_insert, steadySize);
if ((insert_index != -1) &&
(where[insert_index].frequency != TNV_DEFAULT_CLEAR_VALUE)){
where[insert_index].frequency += times_met;
}else{
ValueT temp_min_value;
U_32 temp_min_index, temp_min_freq;
insert_index = find(clear_part, value_to_insert, clearSize);
if (insert_index != -1){
clear_part[insert_index].frequency = clear_part[insert_index].frequency + times_met;
temp_min_index = findMinIdx(where, steadySize);
if (clear_part[insert_index].frequency > where[temp_min_index].frequency){
temp_min_value = where[temp_min_index].value;
temp_min_freq = where[temp_min_index].frequency;
where[temp_min_index].value = clear_part[insert_index].value;
where[temp_min_index].frequency = clear_part[insert_index].frequency;
clear_part[insert_index].frequency = TNV_DEFAULT_CLEAR_VALUE;
temp_min_index = findMinIdx(clear_part, clearSize);
if (temp_min_freq > clear_part[temp_min_index].frequency){
clear_part[temp_min_index].value = temp_min_value;
clear_part[temp_min_index].frequency = temp_min_freq;
}
}
} else {
temp_min_index = findMinIdx(where, steadySize);
if (times_met > where[temp_min_index].frequency)
{
temp_min_value = where[temp_min_index].value;
temp_min_freq = where[temp_min_index].frequency;
where[temp_min_index].value = value_to_insert;
where[temp_min_index].frequency = times_met;
temp_min_index = findMinIdx(clear_part, clearSize);
if (temp_min_freq > clear_part[temp_min_index].frequency)
{
clear_part[temp_min_index].value = temp_min_value;
clear_part[temp_min_index].frequency = temp_min_freq;
}
} else {
temp_min_index = findMinIdx(clear_part, clearSize);
if (times_met > clear_part[temp_min_index].frequency){
clear_part[temp_min_index].value = value_to_insert;
clear_part[temp_min_index].frequency = times_met;
}
}
}
}
}
void TNVTableDividedManager::addNewValue(ValueMethodProfile* methProfile,
VPData* instProfile, ValueT curr_value)
{
methProfile->lockProfile();
struct Simple_TNV_Table* clear_part = instProfile->TNV_clear_part;
U_32* profile_tick = &(instProfile->profile_tick);
if (*profile_tick == clearInterval){
*profile_tick = 0;
clearTopElements(clear_part);
}
(*profile_tick)++;
ValueT* last_value = &(instProfile->last_value);
U_32* num_times_profiled = &(instProfile->num_times_profiled);
if (curr_value == *last_value){
(*num_times_profiled)++;
} else {
flushLastValueCounter(instProfile);
*num_times_profiled = 1;
struct Simple_TNV_Table* steady_part = instProfile->TNV_Table;
insert(steady_part, clear_part, curr_value, *num_times_profiled);
*last_value = curr_value;
}
methProfile->unlockProfile();
}
//------------------------------------------------------------------------------
ValueMethodProfile* ValueProfileCollector::createProfile
(Method_Handle mh, U_32 numkeys, U_32 keys[])
{
port_mutex_lock(&profilesLock);
ValueMethodProfile* profile = new ValueMethodProfile(this, mh);
// Allocate space for value maps
for (U_32 index = 0; index < numkeys; index++){
VPInstructionProfileData* profileData =
getTnvMgr()->createProfileData();
U_32 key = keys[index];
(profile->ValueMap)[key] = profileData;
}
assert(profilesByMethod.find(mh) == profilesByMethod.end());
profilesByMethod[mh] = profile;
port_mutex_unlock(&profilesLock);
return profile;
}
ValueProfileCollector::ValueProfileCollector(EM_PC_Interface* em, const std::string& name, JIT_Handle genJit,
U_32 _TNV_steady_size, U_32 _TNV_clear_size,
U_32 _clear_interval, algotypes _TNV_algo_type,
ProfileUpdateStrategy update_strategy)
: ProfileCollector(em, name, EM_PCTYPE_VALUE, genJit),
updateStrategy(update_strategy)
{
port_mutex_create(&profilesLock, APR_THREAD_MUTEX_NESTED);
if (_TNV_algo_type == TNV_DIVIDED) {
tnvTableManager = new TNVTableDividedManager
(_TNV_steady_size, _TNV_clear_size, _clear_interval, update_strategy);
}else if (_TNV_algo_type == TNV_FIRST_N) {
tnvTableManager = new TNVTableFirstNManager
(_TNV_steady_size, _TNV_clear_size, _clear_interval, update_strategy);
}
catName = std::string(LOG_DOMAIN) + ".profiler." + name;
loggingEnabled = log_is_info_enabled(LOG_DOMAIN) || log_is_info_enabled(catName.c_str());
INFO2(catName.c_str(), "EM: value profiler intialized: " << name.c_str());
}
ValueProfileCollector::~ValueProfileCollector()
{
ValueProfilesMap::iterator it;
for( it = profilesByMethod.begin(); it != profilesByMethod.end(); it++ ){
ValueMethodProfile* profile = it->second;
delete profile;
}
delete tnvTableManager;
port_mutex_destroy(&profilesLock);
}
MethodProfile* ValueProfileCollector::getMethodProfile(Method_Handle mh) const
{
MethodProfile* res = NULL;
port_mutex_lock(&profilesLock);
ValueProfilesMap::const_iterator it = profilesByMethod.find(mh);
if (it != profilesByMethod.end()) {
res = it->second;
}
port_mutex_unlock(&profilesLock);
return res;
}
//------------------------------------------------------------------------------
ValueMethodProfile::ValueMethodProfile(ValueProfileCollector* pc, Method_Handle mh)
: MethodProfile(pc, mh), updatingState(0)
{
port_mutex_create(&lock, APR_THREAD_MUTEX_DEFAULT);
}
ValueMethodProfile::~ValueMethodProfile()
{
port_mutex_destroy(&lock);
}
void ValueMethodProfile::addNewValue
(U_32 instructionKey, POINTER_SIZE_INT valueToAdd)
{
VPDataMap::const_iterator it = ValueMap.find(instructionKey);
assert(it != ValueMap.end());
getVPC()->getTnvMgr()->addNewValue(this, it->second, valueToAdd);
}
POINTER_SIZE_INT ValueMethodProfile::getResult(U_32 instructionKey)
{
lockProfile();
VPDataMap::const_iterator it = ValueMap.find(instructionKey);
if (it == ValueMap.end()) {
unlockProfile();
return 0;
}
VPInstructionProfileData* _temp_vp = it->second;
assert(_temp_vp);
if (_temp_vp == NULL) {
unlockProfile();
return 0;
}
getVPC()->getTnvMgr()->flushLastValueCounter(_temp_vp);
POINTER_SIZE_INT result = getVPC()->getTnvMgr()->findMax(_temp_vp->TNV_Table);
unlockProfile();
return result;
}
void ValueMethodProfile::dumpValues(std::ostream& os)
{
VPDataMap::const_iterator mapIter;
assert(pc->type == EM_PCTYPE_VALUE);
lockProfile();
os << "===== Value profile dump, " << ValueMap.size() << " element(s) ===" << std::endl;
for (mapIter = ValueMap.begin(); mapIter != ValueMap.end(); mapIter++) {
os << "=== Instruction key: " << mapIter->first;
VPInstructionProfileData* _temp_vp = mapIter->second;
TNVTableManager* tnvMgr = getVPC()->getTnvMgr();
tnvMgr->flushLastValueCounter(_temp_vp);
tnvMgr->dumpValues(_temp_vp, os);
}
unlockProfile();
os << "====== End of dump ======================" << std::endl;
}
ValueProfileCollector* ValueMethodProfile::getVPC() const {
assert(pc->type == EM_PCTYPE_VALUE);
return ((ValueProfileCollector*)pc);
}
//------------------------------------------------------------------------------
POINTER_SIZE_INT value_profiler_get_top_value(Method_Profile_Handle mph, U_32 instructionKey)
{
assert(mph != NULL);
MethodProfile* mp = (MethodProfile*)mph;
assert(mp->pc->type == EM_PCTYPE_VALUE);
ValueMethodProfile* vmp = (ValueMethodProfile*)mp;
return vmp->getResult(instructionKey);
}
void value_profiler_add_value(Method_Profile_Handle mph, U_32 instructionKey, POINTER_SIZE_INT valueToAdd)
{
assert(mph != NULL);
MethodProfile* mp = (MethodProfile*)mph;
assert(mp->pc->type == EM_PCTYPE_VALUE);
ValueMethodProfile* vmp = (ValueMethodProfile*)mp;
return vmp->addNewValue(instructionKey, valueToAdd);
}
void value_profiler_dump_values(Method_Profile_Handle mph, std::ostream& os)
{
assert(mph != NULL);
MethodProfile* mp = (MethodProfile*)mph;
assert(mp->pc->type == EM_PCTYPE_VALUE);
ValueMethodProfile* vmp = (ValueMethodProfile*)mp;
vmp->dumpValues(os);
}
Method_Profile_Handle value_profiler_create_profile(PC_Handle pch, Method_Handle mh, U_32 numkeys, U_32 keys[])
{
assert(pch!=NULL);
ProfileCollector* pc = (ProfileCollector*)pch;
assert(pc->type == EM_PCTYPE_VALUE);
ValueMethodProfile* profile = ((ValueProfileCollector*)pc)->createProfile(mh, numkeys, keys);
return (Method_Profile_Handle)profile;
}