Skip to content

Commit

Permalink
DPL: optimise signposts implementation
Browse files Browse the repository at this point in the history
Move out of line the implementation for the Logger case.
  • Loading branch information
ktf committed Aug 16, 2023
1 parent 4efcfbd commit 814f8c3
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 64 deletions.
6 changes: 6 additions & 0 deletions Framework/Foundation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/Framework

o2_add_library(FrameworkFoundation
SOURCES src/RuntimeError.cxx
src/Signpost.cxx
TARGETVARNAME targetName
PUBLIC_LINK_LIBRARIES O2::FrameworkFoundation3rdparty
)
Expand All @@ -39,9 +40,14 @@ add_executable(o2-test-framework-Signpost
test/test_Signpost.cxx)
target_link_libraries(o2-test-framework-Signpost PRIVATE O2::FrameworkFoundation)

add_executable(o2-test-framework-SignpostLogger
test/test_SignpostLogger.cxx)
target_link_libraries(o2-test-framework-SignpostLogger PRIVATE O2::FrameworkFoundation)

get_filename_component(outdir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../tests ABSOLUTE)
set_property(TARGET o2-test-framework-foundation PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir})
set_property(TARGET o2-test-framework-Signpost PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir})
set_property(TARGET o2-test-framework-SignpostLogger PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir})

add_test(NAME framework:foundation COMMAND o2-test-framework-foundation)

Expand Down
144 changes: 81 additions & 63 deletions Framework/Foundation/include/Framework/Signpost.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,77 +55,17 @@
// the signpost information to the log.
#include <atomic>
#include <array>
#include <cstdio>
#include "Framework/RuntimeError.h"

#include <cassert>
#include <atomic>
#include <cstdarg>
#include <cinttypes>

namespace {
namespace
{
struct _o2_lock_free_stack {
static constexpr size_t N = 1024;
std::atomic<size_t> top = 0;
int stack[N];
};

// returns true if the push was successful, false if the stack was full
// @param spin if true, will spin until the stack is not full
bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin = false)
{
size_t currentTop = stack.top.load(std::memory_order_relaxed);
while (true) {
if (currentTop == _o2_lock_free_stack::N && spin == false) {
return false;
} else if (currentTop == _o2_lock_free_stack::N) {
// Avoid consuming too much CPU time if we are spinning.
#if defined(__x86_64__) || defined(__i386__)
__asm__ __volatile__("pause" ::
: "memory");
#elif defined(__aarch64__)
__asm__ __volatile__("yield" ::
: "memory");
#endif
continue;
}

if (stack.top.compare_exchange_weak(currentTop, currentTop + 1,
std::memory_order_release,
std::memory_order_relaxed)) {
stack.stack[currentTop] = value;
return true;
}
}
}

bool _o2_lock_free_stack_pop(_o2_lock_free_stack& stack, int& value, bool spin = false)
{
size_t currentTop = stack.top.load(std::memory_order_relaxed);
while (true) {
if (currentTop == 0 && spin == false) {
return false;
} else if (currentTop == 0) {
// Avoid consuming too much CPU time if we are spinning.
#if defined(__x86_64__) || defined(__i386__)
__asm__ __volatile__("pause" ::
: "memory");
#elif defined(__aarch64__)
__asm__ __volatile__("yield" ::
: "memory");
#endif
continue;
}

if (stack.top.compare_exchange_weak(currentTop, currentTop - 1,
std::memory_order_acquire,
std::memory_order_relaxed)) {
value = stack.stack[currentTop - 1];
return true;
}
}
}

// A log is simply an inbox which keeps track of the available id, so that we can print out different signposts
// with different indentation levels.
// supports up to 1024 paralle signposts before it spinlocks.
Expand Down Expand Up @@ -162,6 +102,18 @@ struct _o2_log_t {
std::atomic<int> stacktrace = 1;
};

bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin = false);
bool _o2_lock_free_stack_pop(_o2_lock_free_stack& stack, int& value, bool spin = false);
//_o2_signpost_id_t _o2_signpost_id_generate_local(_o2_log_t* log);
//_o2_signpost_id_t _o2_signpost_id_make_with_pointer(_o2_log_t* log, void* pointer);
_o2_signpost_index_t o2_signpost_id_make_with_pointer(_o2_log_t* log, void* pointer);
_o2_log_t* _o2_log_create(char const* name, int stacktrace);
void _o2_signpost_event_emit(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
void _o2_signpost_interval_begin(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
void _o2_signpost_interval_end_v(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, va_list args);
void _o2_signpost_interval_end(_o2_log_t* log, _o2_signpost_id_t id, char const* name, char const* const format, ...);
void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace);

// This generates a unique id for a signpost. Do not use this directly, use O2_SIGNPOST_ID_GENERATE instead.
// Notice that this is only valid on a given computer.
// This is guaranteed to be unique at 5 GHz for at least 63 years, if my math is correct.
Expand Down Expand Up @@ -190,6 +142,71 @@ inline _o2_signpost_index_t o2_signpost_id_make_with_pointer(_o2_log_t* log, voi
log->ids[signpost_index].id = (int64_t)pointer;
return signpost_index;
}
} // namespace

// Implementation start here. Include this file with O2_SIGNPOST_IMPLEMENTATION defined in one file of your
// project.
#ifdef O2_SIGNPOST_IMPLEMENTATION
#include <cstdarg>
#include <cstdio>
#include "Framework/RuntimeError.h"
namespace
{
// returns true if the push was successful, false if the stack was full
// @param spin if true, will spin until the stack is not full
bool _o2_lock_free_stack_push(_o2_lock_free_stack& stack, const int& value, bool spin)
{
size_t currentTop = stack.top.load(std::memory_order_relaxed);
while (true) {
if (currentTop == _o2_lock_free_stack::N && spin == false) {
return false;
} else if (currentTop == _o2_lock_free_stack::N) {
// Avoid consuming too much CPU time if we are spinning.
#if defined(__x86_64__) || defined(__i386__)
__asm__ __volatile__("pause" ::
: "memory");
#elif defined(__aarch64__)
__asm__ __volatile__("yield" ::
: "memory");
#endif
continue;
}

if (stack.top.compare_exchange_weak(currentTop, currentTop + 1,
std::memory_order_release,
std::memory_order_relaxed)) {
stack.stack[currentTop] = value;
return true;
}
}
}

bool _o2_lock_free_stack_pop(_o2_lock_free_stack& stack, int& value, bool spin)
{
size_t currentTop = stack.top.load(std::memory_order_relaxed);
while (true) {
if (currentTop == 0 && spin == false) {
return false;
} else if (currentTop == 0) {
// Avoid consuming too much CPU time if we are spinning.
#if defined(__x86_64__) || defined(__i386__)
__asm__ __volatile__("pause" ::
: "memory");
#elif defined(__aarch64__)
__asm__ __volatile__("yield" ::
: "memory");
#endif
continue;
}

if (stack.top.compare_exchange_weak(currentTop, currentTop - 1,
std::memory_order_acquire,
std::memory_order_relaxed)) {
value = stack.stack[currentTop - 1];
return true;
}
}
}

_o2_log_t* _o2_log_create(char const* name, int stacktrace)
{
Expand Down Expand Up @@ -318,7 +335,8 @@ void _o2_log_set_stacktrace(_o2_log_t* log, int stacktrace)
{
log->stacktrace = stacktrace;
}
}
} // anonymous namespace
#endif // O2_SIGNPOST_IMPLEMENTATION

/// Dynamic logs need to be enabled via the O2_LOG_ENABLE_DYNAMIC macro. Notice this will only work
/// for the logger based logging, since the Apple version needs instruments to enable them.
Expand Down
14 changes: 14 additions & 0 deletions Framework/Foundation/src/Signpost.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#define O2_SIGNPOST_IMPLEMENTATION
#include "Framework/Signpost.h"

1 change: 0 additions & 1 deletion Framework/Foundation/test/test_Signpost.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#define O2_FORCE_LOGGER_SIGNPOST 1
#include "Framework/Signpost.h"
#include <iostream>

Expand Down
50 changes: 50 additions & 0 deletions Framework/Foundation/test/test_SignpostLogger.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.


#ifdef __APPLE__
#define O2_SIGNPOST_IMPLEMENTATION
#endif
#define O2_FORCE_LOGGER_SIGNPOST 1
#include "Framework/Signpost.h"
#include <iostream>

int main(int argc, char** argv)
{
O2_DECLARE_LOG(test_Signpost, "my category");
O2_DECLARE_DYNAMIC_LOG(test_SignpostDynamic);

O2_LOG_DEBUG(test_Signpost, "%s %d", "test_Signpost", 1);
O2_SIGNPOST_ID_GENERATE(id, test_Signpost);
O2_SIGNPOST_ID_GENERATE(id2, test_Signpost);
O2_SIGNPOST_START(test_Signpost, id, "Test category", "This is a test signpost");
O2_SIGNPOST_START(test_Signpost, id2, "Test category", "A sepaarate interval");
O2_SIGNPOST_EVENT_EMIT(test_Signpost, id, "Test category", "An event in an interval");
O2_SIGNPOST_END(test_Signpost, id, "Test category", "End of the first interval");
O2_SIGNPOST_END(test_Signpost, id2, "Test category", "A sepaarate interval");
O2_SIGNPOST_ID_FROM_POINTER(id3, test_Signpost, &id2);
O2_SIGNPOST_START(test_Signpost, id3, "Test category", "A signpost interval from a pointer");
O2_SIGNPOST_END(test_Signpost, id3, "Test category", "A signpost interval from a pointer");

// This has an engineering type, which we will not use on Linux / FairLogger
O2_SIGNPOST_ID_FROM_POINTER(id4, test_Signpost, &id3);
O2_SIGNPOST_START(test_Signpost, id4, "Test category", "A signpost with an engineering type formatter " O2_ENG_TYPE(size - in - bytes, "d"), 1);
O2_SIGNPOST_END(test_Signpost, id4, "Test category", "A signpost interval from a pointer");

O2_SIGNPOST_START(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will not see, because they are off by default");
O2_SIGNPOST_END(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will not see, because they are off by default");
O2_LOG_ENABLE_DYNAMIC(test_SignpostDynamic);
#ifdef __APPLE__
// On Apple there is no way to turn on signposts in the logger, so we do not display this message
O2_SIGNPOST_START(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will see, because we turned them on");
O2_SIGNPOST_END(test_SignpostDynamic, id, "Test category", "This is dynamic signpost which you will see, because we turned them on");
#endif
}

0 comments on commit 814f8c3

Please sign in to comment.