352 lines
9.9 KiB
C++
352 lines
9.9 KiB
C++
/*
|
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
*
|
|
* openGauss is licensed under Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
*
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PSL v2 for more details.
|
|
* ---------------------------------------------------------------------------------------
|
|
*
|
|
* vecstore.h
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/include/vecexecutor/vecstore.h
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#ifndef VECSTORE_H
|
|
#define VECSTORE_H
|
|
|
|
#include "executor/executor.h"
|
|
#include "nodes/execnodes.h"
|
|
#include "pgstat.h"
|
|
#include "storage/buf/buffile.h"
|
|
#include "utils/memutils.h"
|
|
#include "utils/memprot.h"
|
|
#include "utils/datum.h"
|
|
#include "vecexecutor/vectorbatch.h"
|
|
#include "workload/workload.h"
|
|
|
|
/* BatchStore is an opaque type whose details are not known outside
|
|
* batchstore.cpp.
|
|
* If the first sort column is text or numeric type, allocate one
|
|
* more Datum to store the prefixal data for fast compare.
|
|
*/
|
|
|
|
typedef struct MultiColumns {
|
|
Datum* m_values;
|
|
uint8* m_nulls;
|
|
|
|
int idx;
|
|
int size;
|
|
|
|
int GetLen(int colNum);
|
|
|
|
} MultiColumns;
|
|
|
|
/*
|
|
* The data of those column to be sorted or unsorted
|
|
*/
|
|
typedef struct MultiColumnsData {
|
|
|
|
MultiColumns* m_memValues;
|
|
|
|
/*
|
|
* The number of columns
|
|
*/
|
|
int m_colNum;
|
|
|
|
/*
|
|
* The capacity of storing row in memory
|
|
*/
|
|
int m_capacity;
|
|
|
|
/*
|
|
* The current row number in memory
|
|
*/
|
|
int m_memRowNum;
|
|
|
|
/*
|
|
* Initialize variables
|
|
*/
|
|
void Init(int capacity);
|
|
|
|
bool HasFreeSlot();
|
|
|
|
void PutValue(MultiColumns val);
|
|
|
|
inline MultiColumns* GetValue(int row);
|
|
|
|
} MultiColumnsData;
|
|
|
|
class VecStore : public BaseObject {
|
|
public:
|
|
char* GetData(int colNum, MultiColumns* multiColumn);
|
|
void SetData(char* dataPtr, int size, MultiColumns* multiColumn);
|
|
void InitColInfo(VectorBatch* batch);
|
|
|
|
bool HasFreeSlot();
|
|
|
|
void PutValue(MultiColumns multiColumn);
|
|
|
|
void UseMem(int64 size);
|
|
|
|
void FreeMem(int64 size);
|
|
|
|
bool LackMem();
|
|
|
|
bool GrowMemValueSlots(char* opname, int planid, MemoryContext context);
|
|
|
|
void AppendBatch(VectorBatch* batch, MultiColumns& multiColumn, int curRow, bool isfree = true);
|
|
|
|
/*
|
|
* abbreSortOptimize used to mark whether allocate one more Datum for
|
|
* fast compare of two data(text or numeric type)
|
|
*/
|
|
template <bool abbreSortOptimize>
|
|
MultiColumns CopyMultiColumn(VectorBatch* batch, int row)
|
|
{
|
|
MultiColumns multiColValue;
|
|
m_colNum = batch->m_cols;
|
|
if (abbreSortOptimize)
|
|
multiColValue.m_values = (ScalarValue*)palloc((batch->m_cols + 1) * sizeof(Datum));
|
|
else
|
|
multiColValue.m_values = (ScalarValue*)palloc(batch->m_cols * sizeof(Datum));
|
|
UseMem(GetMemoryChunkSpace(multiColValue.m_values));
|
|
|
|
multiColValue.m_nulls = (uint8*)palloc(batch->m_cols * sizeof(uint8));
|
|
UseMem(GetMemoryChunkSpace(multiColValue.m_nulls));
|
|
if (m_addWidth)
|
|
m_colWidth += batch->m_cols * sizeof(Datum) + batch->m_cols * sizeof(uint8);
|
|
multiColValue.size = 0;
|
|
uint8 flag;
|
|
|
|
/*
|
|
* When faced with NULL, we just skip, in the following produce, we do the same thing.
|
|
*/
|
|
for (int col = 0; col < batch->m_cols; ++col) {
|
|
Form_pg_attribute attr = &tupDesc->attrs[col];
|
|
|
|
flag = batch->m_arr[col].m_flag[row];
|
|
multiColValue.m_nulls[col] = flag;
|
|
multiColValue.size += sizeof(uint8);
|
|
|
|
if (!IS_NULL(flag)) {
|
|
if (NeedDecode(col)) {
|
|
ScalarValue val = batch->m_arr[col].m_vals[row];
|
|
Datum v = ScalarVector::Decode(val);
|
|
int typlen = 0;
|
|
if (batch->m_arr[col].m_desc.typeId == NAMEOID) {
|
|
typlen = datumGetSize(v, false, -2);
|
|
} else {
|
|
typlen = VARSIZE_ANY(v);
|
|
}
|
|
multiColValue.m_values[col] = datumCopy(v, attr->attbyval, typlen);
|
|
if (m_addWidth)
|
|
m_colWidth += typlen;
|
|
char* p = DatumGetPointer(multiColValue.m_values[col]);
|
|
UseMem(GetMemoryChunkSpace(p));
|
|
multiColValue.size += datumGetSize(v, attr->attbyval, typlen);
|
|
} else {
|
|
multiColValue.m_values[col] = PointerGetDatum(batch->m_arr[col].m_vals[row]);
|
|
multiColValue.size += sizeof(Datum);
|
|
}
|
|
}
|
|
}
|
|
return multiColValue;
|
|
}
|
|
|
|
FORCE_INLINE bool NeedDecode(int col)
|
|
{
|
|
if (m_colInfo[col].m_desc.encoded)
|
|
return true;
|
|
return false;
|
|
}
|
|
void FreeMultiColumn(MultiColumns* pMultiColumn);
|
|
|
|
public:
|
|
MultiColumnsData m_storeColumns;
|
|
|
|
/*
|
|
* remaining memory allowed, in bytes
|
|
*/
|
|
int64 m_availMem;
|
|
/*
|
|
* total memory allowed, in bytes
|
|
*/
|
|
int64 m_allowedMem;
|
|
ScalarVector* m_colInfo;
|
|
int m_colNum;
|
|
TupleDesc tupDesc;
|
|
int64 m_lastFetchCursor;
|
|
int64 m_curFetchCursor;
|
|
|
|
int64 m_colWidth;
|
|
bool m_addWidth;
|
|
bool m_sysBusy;
|
|
int64 m_maxMem;
|
|
int m_spreadNum;
|
|
int m_planId;
|
|
int m_dop;
|
|
};
|
|
|
|
inline bool MultiColumnsData::HasFreeSlot()
|
|
{
|
|
return m_memRowNum < m_capacity - 1;
|
|
}
|
|
|
|
inline void MultiColumnsData::PutValue(MultiColumns val)
|
|
{
|
|
m_memValues[m_memRowNum] = val;
|
|
++m_memRowNum;
|
|
}
|
|
|
|
inline int MultiColumns::GetLen(int colNum)
|
|
{
|
|
return size;
|
|
}
|
|
|
|
inline MultiColumns* MultiColumnsData::GetValue(int row)
|
|
{
|
|
return m_memValues + row;
|
|
}
|
|
|
|
inline void VecStore::UseMem(int64 size)
|
|
{
|
|
m_availMem -= size;
|
|
}
|
|
|
|
inline void VecStore::FreeMem(int64 size)
|
|
{
|
|
m_availMem += size;
|
|
}
|
|
|
|
inline bool VecStore::LackMem()
|
|
{
|
|
int64 usedMem = m_allowedMem - m_availMem;
|
|
|
|
if (m_availMem < 0 || gs_sysmemory_busy(usedMem * m_dop, true))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
inline bool VecStore::HasFreeSlot()
|
|
{
|
|
return m_storeColumns.HasFreeSlot();
|
|
}
|
|
|
|
inline bool VecStore::GrowMemValueSlots(char* opname, int planid, MemoryContext context)
|
|
{
|
|
double grow_ratio =
|
|
Min(DEFAULT_GROW_RATIO, ((double)(MaxAllocSize / sizeof(MultiColumns))) / (double)m_storeColumns.m_capacity);
|
|
double unspread_ratio = grow_ratio;
|
|
bool need_spread = false;
|
|
int64 mem_used = m_allowedMem - m_availMem;
|
|
|
|
if (m_availMem < mem_used)
|
|
unspread_ratio = Min(unspread_ratio, (double)m_allowedMem / mem_used);
|
|
|
|
/* No grow of rows, so return */
|
|
if (m_storeColumns.m_capacity * unspread_ratio <= m_storeColumns.m_capacity) {
|
|
if (m_maxMem < m_allowedMem)
|
|
return false;
|
|
else
|
|
need_spread = true;
|
|
} else
|
|
grow_ratio = unspread_ratio;
|
|
|
|
if (gs_sysmemory_busy((m_allowedMem - m_availMem) * m_dop, true)) {
|
|
m_sysBusy = true;
|
|
AllocSet sortContext = (AllocSet)context;
|
|
sortContext->maxSpaceSize = m_allowedMem - m_availMem;
|
|
m_allowedMem = m_allowedMem - m_availMem;
|
|
MEMCTL_LOG(LOG,
|
|
"%s(%d) early spilled, workmem: %ldKB, freemem: %ldKB.",
|
|
opname,
|
|
planid,
|
|
m_allowedMem / 1024L,
|
|
m_availMem / 1024L);
|
|
pgstat_add_warning_early_spill();
|
|
return false;
|
|
}
|
|
|
|
/* For concurrent case, we don't allow sort/materialize to spread a lot */
|
|
if (need_spread && (m_spreadNum < 2 || g_instance.wlm_cxt->stat_manager.comp_count == 1)) {
|
|
if (m_availMem < 0)
|
|
m_allowedMem -= m_availMem;
|
|
int64 spreadMem = Min(Min(dywlm_client_get_memory() * 1024L, m_allowedMem), m_maxMem - m_allowedMem);
|
|
if (spreadMem <= m_allowedMem * MEM_AUTO_SPREAD_MIN_RATIO) {
|
|
MEMCTL_LOG(LOG,
|
|
"%s(%d) auto mem spread %ldKB failed, and work mem is %ldKB.",
|
|
opname,
|
|
planid,
|
|
spreadMem / 1024L,
|
|
m_allowedMem / 1024L);
|
|
if (m_spreadNum > 0) {
|
|
pgstat_add_warning_spill_on_memory_spread();
|
|
}
|
|
return false;
|
|
}
|
|
grow_ratio = Min(grow_ratio, 1 + (double)spreadMem / m_allowedMem);
|
|
m_allowedMem += spreadMem;
|
|
m_availMem = spreadMem;
|
|
AllocSet sortContext = (AllocSet)context;
|
|
sortContext->maxSpaceSize += spreadMem;
|
|
m_spreadNum++;
|
|
MEMCTL_LOG(DEBUG2,
|
|
"%s(%d) auto mem spread %ldKB succeed, and work mem is %ldKB.",
|
|
opname,
|
|
planid,
|
|
spreadMem / 1024L,
|
|
m_allowedMem / 1024L);
|
|
}
|
|
|
|
/* if there's no more space after grow, then fail */
|
|
if (m_storeColumns.m_capacity * grow_ratio <= m_storeColumns.m_capacity) {
|
|
MEMCTL_LOG(LOG, "%s(%d) mem limit reached.", opname, planid);
|
|
return false;
|
|
}
|
|
|
|
if (!gs_sysmemory_avail(mem_used * (grow_ratio - 1))) {
|
|
MEMCTL_LOG(LOG,
|
|
"%s(%d) mem lack, workmem: %ldKB, freemem: %ldKB,"
|
|
"usedmem: %ldKB, grow ratio: %.2f.",
|
|
opname,
|
|
planid,
|
|
m_allowedMem / 1024L,
|
|
m_availMem / 1024L,
|
|
mem_used / 1024L,
|
|
grow_ratio);
|
|
return false;
|
|
}
|
|
|
|
FreeMem(GetMemoryChunkSpace(m_storeColumns.m_memValues));
|
|
|
|
m_storeColumns.m_capacity *= grow_ratio;
|
|
m_storeColumns.m_memValues =
|
|
(MultiColumns*)repalloc(m_storeColumns.m_memValues, m_storeColumns.m_capacity * sizeof(MultiColumns));
|
|
|
|
UseMem(GetMemoryChunkSpace(m_storeColumns.m_memValues));
|
|
|
|
if (m_availMem < 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
inline void VecStore::PutValue(MultiColumns multiColValue)
|
|
{
|
|
m_storeColumns.PutValue(multiColValue);
|
|
}
|
|
|
|
#endif /* VECSTORE_H */
|