Skip to content

Commit

Permalink
Add memory profiling to multio-server
Browse files Browse the repository at this point in the history
  • Loading branch information
raguridan committed Aug 7, 2024
1 parent e16dd40 commit b0daf8d
Show file tree
Hide file tree
Showing 8 changed files with 379 additions and 5 deletions.
8 changes: 6 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ ecbuild_add_option( FEATURE HEALPIX_EXAMPLES

### Definitions used by the output manager
ecbuild_add_option( FEATURE OUTPUT_MANAGER
DEFAULT ON
DEFAULT OFF
DESCRIPTION "Build the output manager" )

ecbuild_add_option( FEATURE OUTPUT_MANAGER_ENCODER_REPORT
Expand All @@ -109,9 +109,13 @@ ecbuild_add_option( FEATURE OUTPUT_MANAGER_ENCODER_REPORT

ecbuild_add_option( FEATURE OUTPUT_MANAGER_TOOL
CONDITION HAVE_OUTPUT_MANAGER
DEFAULT ON
DEFAULT OFF
DESCRIPTION "Enable generation of standalone tool" )

ecbuild_add_option( FEATURE MULTIO_SERVER_MEMORY_PROFILE
DEFAULT OFF
DESCRIPTION "Enable multio server memory profiling" )

### export package info

set( MULTIO_CONFIG_DIR share/multio/config )
Expand Down
17 changes: 14 additions & 3 deletions src/multio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,20 @@ list( APPEND multio_util_srcs
util/Substitution.cc
util/Substitution.h
util/BinaryUtils.h
util/RingBuffer.h
util/Tracer.h
util/Tracer.cc
)

if ( ENABLE_MULTIO_SERVER_MEMORY_PROFILE )
list( APPEND multio_util_srcs
util/RingBuffer.h
util/Tracer.h
util/Tracer.cc
util/TraceEventIds.h
util/MemoryInformation.h
util/MemoryInformation.cc
)
set(multio_utils_definitions "MULTIO_SERVER_MEMORY_PROFILE_ENABLED")
endif()

list( APPEND multio_config_srcs
config/ComponentConfiguration.cc
config/ComponentConfiguration.h
Expand Down Expand Up @@ -187,6 +196,8 @@ ecbuild_add_library(
${METKIT_INCLUDE_DIRS}
${ECKIT_INCLUDE_DIRS}

PRIVATE_DEFINITIONS ${multio_utils_definitions}

PUBLIC_LIBS
metkit eckit eckit_mpi)

Expand Down
90 changes: 90 additions & 0 deletions src/multio/server/Listener.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,72 @@
#include "multio/transport/TransportRegistry.h"
#include "multio/util/ScopedThread.h"

#ifdef MULTIO_SERVER_MEMORY_PROFILE_ENABLED

#include "multio/util/MemoryInformation.h"
#include "multio/util/TraceEventIds.h"
#include "multio/util/Tracer.h"

using namespace multio::util;

namespace {
const auto tracerMemoryReportPeriod = std::chrono::seconds(10);
const auto tracerFlushPeriod = std::chrono::minutes(10);

const auto tracerNumberOfChunks = 8;
const auto tracerEventsPerChunk = 32768;

const auto tracerValueMask = 0xFFFFFFFFULL;
const auto unitShiftAmount = 32;

const std::unordered_map<multio::util::InformationTypes, uint64_t> infoTypeToTraceIdMapping = {
{ multio::util::InformationTypes::PeakVirtualMemory, MULTIO_PEAK_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::VirtualMemory, MULTIO_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::LockedVirtualMemory, MULTIO_LOCKED_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::PinnedVirtualMemory, MULTIO_PINNED_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::MaximumResidentMemory, MULTIO_MAXIMUM_RESIDENT_MEMORY },
{ multio::util::InformationTypes::ResidentMemory, MULTIO_RESIDENT_MEMORY },
{ multio::util::InformationTypes::AnonimousResidentMemory, MULTIO_ANONIMOUS_RESIDENT_MEMORY },
{ multio::util::InformationTypes::FileMappingResidentMemory, MULTIO_FILE_MAPPING_RESIDENT_MEMORY },
{ multio::util::InformationTypes::SharedResidentMemory, MULTIO_SHARED_RESIDENT_MEMORY },
{ multio::util::InformationTypes::DataVirtualMemory, MULTIO_DATA_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::StackVirtualMemory, MULTIO_STACK_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::TextSegmentVirtualMemory, MULTIO_TEXT_SEGMENT_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::SharedLibraryTextVirtualMemory, MULTIO_SHARED_LIBRARY_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::PageTableEntryVirtualMemory, MULTIO_PAGE_TABLE_ENTRY_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::SecondLevelPageTableEntryVirtualMemory, MULTIO_SECOND_LEVEL_PAGE_TABLE_ENTRY_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::SwappedOutVirtualMemory, MULTIO_SWAPPED_OUT_VIRTUAL_MEMORY },
{ multio::util::InformationTypes::HugeTablesMemory, MULTIO_HUGE_TABLE_MEMORY },
};

const std::unordered_map<multio::util::InformationSizeUnits, uint64_t> sizeUnitToTraceValueMapping = {
{ multio::util::InformationSizeUnits::Bytes, 0},
{ multio::util::InformationSizeUnits::KiloBytes, 1},
{ multio::util::InformationSizeUnits::MegaBytes, 2},
{ multio::util::InformationSizeUnits::GigaBytes, 3},
};

multio::util::Tracer tracer(tracerNumberOfChunks, tracerEventsPerChunk, "./multio_memory.bin");

void reportMemoryUsage() {
const multio::util::MemoryInformation usage;
const auto keys = usage.getAvailableKeys();

for (const auto key : keys) {
const auto item = usage.get(key);

const auto id = infoTypeToTraceIdMapping.at(key);
const auto value = item.Value & tracerValueMask;
const uint64_t unit = sizeUnitToTraceValueMapping.at(item.Unit) << unitShiftAmount;

tracer.recordEvent(id | unit | value);
}
}

}

#endif

namespace multio::server {

using message::Message;
Expand Down Expand Up @@ -109,8 +175,32 @@ void Listener::start() {

void Listener::listen() {
withFailureHandling([this]() {

#ifdef MULTIO_SERVER_MEMORY_PROFILE_ENABLED
tracer.startWriterThread();

auto last_report_time = std::chrono::system_clock::now();
auto last_flush_time = std::chrono::system_clock::now();
#endif

do {
transport_.listen();

#ifdef MULTIO_SERVER_MEMORY_PROFILE_ENABLED
const auto current_time = std::chrono::system_clock::now();
const auto elapsed_from_report = current_time - last_report_time;
const auto elapsed_from_flush = current_time - last_flush_time;

if (elapsed_from_report > tracerMemoryReportPeriod) {
reportMemoryUsage();
last_report_time = current_time;
}

if (elapsed_from_flush > tracerFlushPeriod) {
tracer.flushCurrentChunk();
last_flush_time = current_time;
}
#endif
} while (msgQueue_.checkInterrupt() && !msgQueue_.closed());
});
}
Expand Down
120 changes: 120 additions & 0 deletions src/multio/util/MemoryInformation.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include "MemoryInformation.h"

#include "eckit/exception/Exceptions.h"

#include <regex>
#include <ios>
#include <sstream>

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

namespace {
using namespace multio::util;

const std::map<InformationTypes, std::regex> extractors = {
{ InformationTypes::PeakVirtualMemory, std::regex("VmPeak:\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::VirtualMemory, std::regex("VmSize:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::LockedVirtualMemory, std::regex("VmLck:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::PinnedVirtualMemory, std::regex("VmPin:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::MaximumResidentMemory, std::regex("VmHWM:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::ResidentMemory, std::regex("VmRSS:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::AnonimousResidentMemory, std::regex("RssAnon:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::FileMappingResidentMemory, std::regex("RssFile:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::SharedResidentMemory, std::regex("RssShmem:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::DataVirtualMemory, std::regex("VmData:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::StackVirtualMemory, std::regex("VmStk:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::TextSegmentVirtualMemory, std::regex("VmExe:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::SharedLibraryTextVirtualMemory, std::regex("VmLib:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::PageTableEntryVirtualMemory, std::regex("VmPTE:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::SecondLevelPageTableEntryVirtualMemory, std::regex("VmPMD:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::SwappedOutVirtualMemory, std::regex("VmSwap:..\\s*(\\d*)\\s(\\w*)") },
{ InformationTypes::HugeTablesMemory, std::regex("HugetlbPages:..\\s*(\\d*)\\s(\\w*)") },
};

InformationSizeUnits toSizeUnit(const std::string& unit) {
switch (std::toupper(unit[0])) {
case 'K':
return InformationSizeUnits::KiloBytes;
case 'M':
return InformationSizeUnits::MegaBytes;
case 'G':
return InformationSizeUnits::GigaBytes;
default:
return InformationSizeUnits::Bytes;
}
}

std::map<InformationTypes, InformationItem> createMemoryInformation() {
const auto maximumStatusStringLengthInChars = 16 * 1024;

std::map<InformationTypes, InformationItem> information;
int fd = -1;

try {
const auto path = "/proc/self/status";

fd = open(path, O_RDONLY);
if (fd < 0) {
throw eckit::FailedSystemCall("open", Here());
}

auto mem = reinterpret_cast<char *>(alloca(maximumStatusStringLengthInChars));

ssize_t read_bytes = read(fd, mem, maximumStatusStringLengthInChars);
ssize_t already_read = read_bytes;
while ((read_bytes > 0) && (already_read < maximumStatusStringLengthInChars)) {
read_bytes = read(fd, mem + already_read, maximumStatusStringLengthInChars - already_read);
if ((read_bytes < 0) && (errno != EAGAIN)) {
throw eckit::FailedSystemCall("read", Here());
}

already_read += read_bytes;
}

close(fd);
fd = -1;

auto statusContents = std::istringstream(mem);

auto line = reinterpret_cast<char *>(alloca(maximumStatusStringLengthInChars));
while (statusContents.getline(line, maximumStatusStringLengthInChars)) {
const auto lineText = std::string(line);
for (auto const& extractor : extractors) {
std::smatch match;
if (std::regex_search(lineText, match, extractor.second)) {
information.emplace(extractor.first,
InformationItem { std::atoi(match[1].str().c_str()), toSizeUnit(match[2].str()) });
}
}
}
} catch (...) {
if (fd >= 0) {
close(fd);
fd = -1;
}

throw;
}

return information;
}
}

namespace multio::util {

MemoryInformation::MemoryInformation() : memInfo_(createMemoryInformation()) {}

std::vector<InformationTypes> MemoryInformation::getAvailableKeys() const {
std::vector<InformationTypes> keys;
keys.reserve(memInfo_.size());

for (const auto& item : memInfo_) {
keys.emplace_back(item.first);
}

return keys;
}

}
74 changes: 74 additions & 0 deletions src/multio/util/MemoryInformation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* (C) Copyright 1996- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/

/// @author Razvan Aguridan

/// @date Jun 2024

#pragma once

#include <cstdint>
#include <map>
#include <vector>

namespace multio::util {

enum class InformationTypes {
PeakVirtualMemory,
VirtualMemory,
LockedVirtualMemory,
PinnedVirtualMemory,
MaximumResidentMemory,
ResidentMemory,
AnonimousResidentMemory,
FileMappingResidentMemory,
SharedResidentMemory,
DataVirtualMemory,
StackVirtualMemory,
TextSegmentVirtualMemory,
SharedLibraryTextVirtualMemory,
PageTableEntryVirtualMemory,
SecondLevelPageTableEntryVirtualMemory,
SwappedOutVirtualMemory,
HugeTablesMemory
};

enum class InformationSizeUnits {
Bytes,
KiloBytes,
MegaBytes,
GigaBytes
};

struct InformationItem {
uint64_t Value;
InformationSizeUnits Unit;
};

class MemoryInformation {
public:
MemoryInformation();
~MemoryInformation() = default;

std::vector<InformationTypes> getAvailableKeys() const;

const InformationItem& get(InformationTypes info) const { return memInfo_.at(info); }

private:
MemoryInformation(MemoryInformation const&) = delete;
MemoryInformation& operator=(MemoryInformation const&) = delete;

MemoryInformation(MemoryInformation const&&) = delete;
MemoryInformation& operator=(MemoryInformation const&&) = delete;

const std::map<InformationTypes, InformationItem> memInfo_;
};

} // namespace multio::util
Loading

0 comments on commit b0daf8d

Please sign in to comment.