From fb686346d602bae6495aada275fd6948968eaa33 Mon Sep 17 00:00:00 2001 From: Tianyu Date: Sun, 24 Mar 2024 03:46:58 +0000 Subject: [PATCH 01/23] feat(stream): Add serialization functions Add serialization and deserialization functions for the Faasm state interface. This allows for arbitrary data to be stored. --- libfaasm/CMakeLists.txt | 2 ++ libfaasm/faasm/serialization.h | 18 +++++++++++ libfaasm/serialization.cpp | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 libfaasm/faasm/serialization.h create mode 100644 libfaasm/serialization.cpp diff --git a/libfaasm/CMakeLists.txt b/libfaasm/CMakeLists.txt index d17616a..e90d9ca 100644 --- a/libfaasm/CMakeLists.txt +++ b/libfaasm/CMakeLists.txt @@ -21,6 +21,7 @@ set(PUBLIC_HEADERS faasm/shared_mem.h faasm/state.h faasm/time.h + faasm/serialization.h ) set(LIB_FILES @@ -33,6 +34,7 @@ set(LIB_FILES state.cpp time.cpp zygote.cpp + serialization.cpp ) if (CMAKE_SYSTEM_NAME STREQUAL "WASI") diff --git a/libfaasm/faasm/serialization.h b/libfaasm/faasm/serialization.h new file mode 100644 index 0000000..714c5e6 --- /dev/null +++ b/libfaasm/faasm/serialization.h @@ -0,0 +1,18 @@ +#ifndef FAASM_SERIALIZATION_H +#define FAASM_SERIALIZATION_H +#include +#include +#include +#include +namespace faasm { +// Serialiazion and Deserialization from uint8_t vetors to uint32_t +uint32_t uint8VToUint32(const std::vector& bytes); +std::vector uint32ToUint8V(uint32_t value); +// Serialiazion and Deserialization from uint8_t vetors to Map +std::vector mapToUint8V(const std::map& map); +std::map uint8VToMap(const std::vector& bytes); +// Serialiazion and Deserialization from uint8_t vetors to Map +std::vector mapIntToUint8V(const std::map& map); +std::map uint8VToMapInt(const std::vector& bytes); +} +#endif \ No newline at end of file diff --git a/libfaasm/serialization.cpp b/libfaasm/serialization.cpp new file mode 100644 index 0000000..69eba80 --- /dev/null +++ b/libfaasm/serialization.cpp @@ -0,0 +1,59 @@ +#include "faasm/serialization.h" + +namespace faasm { +// Transforms a uint8_t vector of bytes into a uint32_t +uint32_t uint8VToUint32(const std::vector& bytes) +{ + uint32_t value = 0; + for (int i = 0; i < 4; i++) { + value = (value << 8) | bytes[i]; + } + return value; +} +// Transforms unint32_t of bytes into a uint8_t vector +std::vector uint32ToUint8V(uint32_t value) +{ + std::vector bytes(4); + for (int i = 0; i < 4; i++) { + bytes[3 - i] = (value >> (i * 8)) & 0xFF; + } + return bytes; +} + +// Transforms a map into a uint8_t vector of bytes. +std::vector mapIntToUint8V(const std::map& map) { + std::vector bytes; + for (const auto& pair : map) { + std::string key = pair.first; + int value = pair.second; + bytes.push_back(static_cast(key.size())); // Key length + bytes.insert(bytes.end(), key.begin(), key.end()); // Key + // Append 'int' value as four bytes + for (int i = 3; i >= 0; --i) { + bytes.push_back((value >> (i * 8)) & 0xFF); + } + } + return bytes; +} + +// Transforms a uint8_t vector of bytes into a map . +std::map uint8VToMapInt(const std::vector& bytes) { + std::map map; + size_t i = 0; + while (i < bytes.size()) { + uint8_t keyLen = bytes[i++]; + std::string key(bytes.begin() + i, bytes.begin() + i + keyLen); + i += keyLen; + // Read 'int' value from four bytes + int value = 0; + for (int j = 0; j < 4; ++j) { + value |= (static_cast(bytes[i + j]) << ((3 - j) * 8)); + } + i += 4; + map[key] = value; + } + return map; +} + + +} \ No newline at end of file From fcb0c49d280e7cd1574875a61a1d3fdb4d9a54de Mon Sep 17 00:00:00 2001 From: Tianyu Date: Sun, 24 Mar 2024 06:10:40 +0000 Subject: [PATCH 02/23] feat(stream): add stream example functions v1 Add a wordcount example without using map in count function. --- func/CMakeLists.txt | 1 + func/stream/CMakeLists.txt | 12 +++++++++++ func/stream/wc_count.cpp | 26 +++++++++++++++++++++++ func/stream/wc_random_source.cpp | 27 ++++++++++++++++++++++++ func/stream/wc_split.cpp | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 func/stream/CMakeLists.txt create mode 100644 func/stream/wc_count.cpp create mode 100644 func/stream/wc_random_source.cpp create mode 100644 func/stream/wc_split.cpp diff --git a/func/CMakeLists.txt b/func/CMakeLists.txt index 47f4f86..d17679c 100644 --- a/func/CMakeLists.txt +++ b/func/CMakeLists.txt @@ -75,3 +75,4 @@ add_subdirectory(errors) add_subdirectory(mpi) add_subdirectory(omp) add_subdirectory(threads) +add_subdirectory(stream) \ No newline at end of file diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt new file mode 100644 index 0000000..4e92a61 --- /dev/null +++ b/func/stream/CMakeLists.txt @@ -0,0 +1,12 @@ +set(FAASM_USER stream) +function(stream_func exec_name dir_path) + faasm_func(${exec_name} ${dir_path}) + set(ALL_STREAM_FUNCS ${ALL_STREAM_FUNCS} ${exec_name} PARENT_SCOPE) +endfunction(stream_func) +stream_func(wc_random_source wc_random_source.cpp) +stream_func(wc_split wc_split.cpp) +stream_func(wc_count wc_count.cpp) +# Custom target to group all the stream functions +add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) +# Turn on WASM profiling +add_definitions(-DWASM_PROF=1) \ No newline at end of file diff --git a/func/stream/wc_count.cpp b/func/stream/wc_count.cpp new file mode 100644 index 0000000..d4da52c --- /dev/null +++ b/func/stream/wc_count.cpp @@ -0,0 +1,26 @@ +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/serialization.h" +#include // For PRIu32 +#include +#include +#include +int main(int argc, char* argv[]) +{ + const char* word = faasm::getStringInput(""); + size_t size = faasmReadStateSize(word); + uint32_t newSize = 1; + if (size != 0) { + std::vector bytes(size); + faasmReadState(word, bytes.data(), size); + uint32_t oldSize = faasm::uint8VToUint32(bytes); + newSize = oldSize + 1; + } + std::vector newBytes = faasm::uint32ToUint8V(newSize); + // Print the word and newSize + printf("WordCount count: The size of [ %s ] are changed to [%" PRIu32 "] \n", + word, + newSize); + faasmWriteState(word, newBytes.data(), newBytes.size()); + return 0; +} \ No newline at end of file diff --git a/func/stream/wc_random_source.cpp b/func/stream/wc_random_source.cpp new file mode 100644 index 0000000..c94978a --- /dev/null +++ b/func/stream/wc_random_source.cpp @@ -0,0 +1,27 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/random.h" +#include +#include +#include +int main(int argc, char* argv[]) +{ + std::vector messages = { + "the cow jumped over the moon", + "an apple a day keeps the doctor away", + "four score and seven years ago", + "snow white and the seven dwarfs", + "i am at two with nature" + }; + // Get a random number from 0 to size of messages + int random_number = faasm::randomInteger(0, messages.size() - 1); + // Return a random sentence + std::string message = messages[random_number]; + printf("WordCount source: Created Random sentence: %s\n", + message.c_str()); + // Call the split function + faasmChainNamed("wc_split", + reinterpret_cast(message.c_str()), + message.size()); + return 0; +} \ No newline at end of file diff --git a/func/stream/wc_split.cpp b/func/stream/wc_split.cpp new file mode 100644 index 0000000..899df25 --- /dev/null +++ b/func/stream/wc_split.cpp @@ -0,0 +1,36 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include +#include +#include +int main(int argc, char* argv[]) +{ + const char* inputCStr = faasm::getStringInput(""); + std::string inputSentence(inputCStr); // Convert to std::string + + // Split the inputSentence by space and store in a vector + std::string word; + if (inputSentence.empty()) { + printf("WordCount Split: No input sentence\n"); + return 0; + } + for (char x : inputSentence) { + if (x == ' ') { + if (!word.empty()) { + faasmChainNamed("wc_count", + reinterpret_cast(word.c_str()), + word.size()); + word.clear(); + } + } else { + word = word + x; + } + } + if (!word.empty()) { + faasmChainNamed("wc_count", + reinterpret_cast(word.c_str()), + word.size()); + } + return 0; +} \ No newline at end of file From df75448c3f430329fef5f671d39969d1cd0d8e2e Mon Sep 17 00:00:00 2001 From: Tianyu Date: Tue, 2 Apr 2024 01:11:02 +0000 Subject: [PATCH 03/23] feat(stream): Add function state v1 Add the local util and intrinsic function for function state. --- func/stream/CMakeLists.txt | 2 + func/stream/function_state.cpp | 76 +++++++++++++++++++++++++++++++++ libfaasm/core.cpp | 15 +++++++ libfaasm/faasm/core.h | 15 +++++++ libfaasm/faasm/host_interface.h | 6 +++ libfaasm/faasm/serialization.h | 13 +++++- libfaasm/libfaasm.imports | 4 ++ libfaasm/serialization.cpp | 55 ++++++++++++++++++++++++ 8 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 func/stream/function_state.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index 4e92a61..a22b0ee 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -6,6 +6,8 @@ endfunction(stream_func) stream_func(wc_random_source wc_random_source.cpp) stream_func(wc_split wc_split.cpp) stream_func(wc_count wc_count.cpp) +stream_func(function_state function_state.cpp) + # Custom target to group all the stream functions add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) # Turn on WASM profiling diff --git a/func/stream/function_state.cpp b/func/stream/function_state.cpp new file mode 100644 index 0000000..5199646 --- /dev/null +++ b/func/stream/function_state.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // Read a function state + size_t size = faasmReadFunctionStateSize(); + // printf the size + printf("Size of Function State: %ld\n", size); + + // It means it is invoked in first time + // Write a function State + std::vector k1 = { 1, 2, 3, 4, 5 }; + std::vector k2 = { 2, 3, 4, 5, 6, 7, 8 }; + std::vector k3 = { 3, 4, 5, 6, 7, 8 }; + + std::map> functionState = + std::map>(); + functionState["k1"] = k1; + functionState["k2"] = k2; + functionState["k3"] = k3; + + std::vector functionStateBytes = + faasm::serializeFuncState(functionState); + faasmWriteFunctionState(functionStateBytes.data(), + functionStateBytes.size()); + + size = faasmReadFunctionStateSize(); + // Read a function state + std::vector buffer(size); + faasmReadFunctionState(buffer.data(), size); + functionState = faasm::deserializeFuncState(buffer); + + // Print the function state + for (auto const& x : functionState) { + std::cout << x.first << " : "; + for (auto const& y : x.second) { + std::cout << +y << " "; + } + std::cout << std::endl; + } + std::vector k4; + k4.reserve(10000); + for (uint16_t i = 1; i <= 10000; ++i) { + k4.push_back(1); + } + functionState["k4"] = k4; + functionStateBytes = faasm::serializeFuncState(functionState); + faasmWriteFunctionState(functionStateBytes.data(), + functionStateBytes.size()); + size = faasmReadFunctionStateSize(); + // Read a function state + std::vector buffer1(size); + faasmReadFunctionState(buffer1.data(), size); + functionState = faasm::deserializeFuncState(buffer1); + + // Print the function state + for (auto const& x : functionState) { + std::cout << x.first << " : "; + for (auto const& y : x.second) { + std::cout << +y << " "; + } + std::cout << std::endl; + } + + // // size_t size = faasmReadFunctionStateSize(); + // // Cleanup + // delete functionState; + + return 0; +} diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index a7b29bd..4780051 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -227,3 +227,18 @@ unsigned int getConfFlag(const char* key) { return __faasm_conf_flag(key); } + +void faasmWriteFunctionState(const uint8_t* data, long dataLen) +{ + __faasm_write_function_state(data, dataLen); +} + +size_t faasmReadFunctionStateSize() +{ + return __faasm_read_function_state(nullptr, 0); +} + +long faasmReadFunctionState(uint8_t* buffer, long bufferLen) +{ + return __faasm_read_function_state(buffer, bufferLen); +} \ No newline at end of file diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index 351b700..9c3cdaa 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -216,6 +216,21 @@ extern "C" */ void faasmBacktrace(const int depth); + /** + * Write the Function level state into State Storage + */ + void faasmWriteFunctionState(const uint8_t* data, long dataLen); + + /** + * Gets the size of the function state + */ + size_t faasmReadFunctionStateSize(); + + /** + * Reads the Function levl full state from State Storage + */ + long faasmReadFunctionState(unsigned char* buffer, long bufferLen); + // Macro for defining zygotes (a default fallback noop is provided) int __attribute__((weak)) _faasm_zygote(); #define FAASM_ZYGOTE() int _faasm_zygote() diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index 00a7573..77f891f 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -160,4 +160,10 @@ void __faasm_migrate_point(FaasmMigrateEntryPoint f, int arg); HOST_IFACE_FUNC void __faasm_host_interface_test(int testNum); + +HOST_IFACE_FUNC +void __faasm_write_function_state(const unsigned char* data, long dataLen); + +HOST_IFACE_FUNC +long __faasm_read_function_state(unsigned char* buffer, long bufferLen); #endif diff --git a/libfaasm/faasm/serialization.h b/libfaasm/faasm/serialization.h index 714c5e6..2763d65 100644 --- a/libfaasm/faasm/serialization.h +++ b/libfaasm/faasm/serialization.h @@ -1,18 +1,27 @@ #ifndef FAASM_SERIALIZATION_H #define FAASM_SERIALIZATION_H #include -#include #include #include +#include namespace faasm { // Serialiazion and Deserialization from uint8_t vetors to uint32_t uint32_t uint8VToUint32(const std::vector& bytes); std::vector uint32ToUint8V(uint32_t value); + // Serialiazion and Deserialization from uint8_t vetors to Map std::vector mapToUint8V(const std::map& map); -std::map uint8VToMap(const std::vector& bytes); +std::map uint8VToMap( + const std::vector& bytes); + // Serialiazion and Deserialization from uint8_t vetors to Map std::vector mapIntToUint8V(const std::map& map); std::map uint8VToMapInt(const std::vector& bytes); + +// Serialiazion and Deserialization from function State to uint8_t vetors +std::vector serializeFuncState( + const std::map>& map); +std::map> deserializeFuncState( + const std::vector& bytes); } #endif \ No newline at end of file diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index 6ca3203..e6e555a 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -44,3 +44,7 @@ __faasm_migrate_point # Test __faasm_host_interface_test + +# Stream +__faasm_write_function_state +__faasm_read_function_state \ No newline at end of file diff --git a/libfaasm/serialization.cpp b/libfaasm/serialization.cpp index 69eba80..afc9082 100644 --- a/libfaasm/serialization.cpp +++ b/libfaasm/serialization.cpp @@ -55,5 +55,60 @@ std::map uint8VToMapInt(const std::vector& bytes) { return map; } +void serializeUInt32(std::vector& vec, uint32_t value) { + vec.insert(vec.end(), { + static_cast(value >> 24), + static_cast(value >> 16), + static_cast(value >> 8), + static_cast(value) + }); +} + +std::vector serializeFuncState(const std::map>& map) { + std::vector serialized; + // Pre-calculate required capacity to minimize reallocations + size_t totalSize = 0; + for (const auto& [key, valueVec] : map) { + totalSize += 4 + key.size() + 4 + valueVec.size(); + } + serialized.reserve(totalSize); + + for (const auto& [key, valueVec] : map) { + serializeUInt32(serialized, key.size()); + serialized.insert(serialized.end(), key.begin(), key.end()); + serializeUInt32(serialized, valueVec.size()); + serialized.insert(serialized.end(), valueVec.begin(), valueVec.end()); + } + + return serialized; +} + +uint32_t deserializeUInt32(const std::vector& vec, size_t& index) { + uint32_t value = + (static_cast(vec[index]) << 24) | + (static_cast(vec[index + 1]) << 16) | + (static_cast(vec[index + 2]) << 8) | + (static_cast(vec[index + 3])); + index += 4; + return value; +} + +std::map> deserializeFuncState(const std::vector& serialized) { + std::map> map; + size_t index = 0; + while (index < serialized.size()) { + auto keyLength = deserializeUInt32(serialized, index); + std::string key(serialized.begin() + index, serialized.begin() + index + keyLength); + index += keyLength; + + auto valueLength = deserializeUInt32(serialized, index); + std::vector valueVec(serialized.begin() + index, serialized.begin() + index + valueLength); + index += valueLength; + + map.emplace(std::move(key), std::move(valueVec)); + } + return map; +} + } \ No newline at end of file From 05c1823ce771055c3b16e24443f8b1f6714826ae Mon Sep 17 00:00:00 2001 From: Tianyu Date: Wed, 3 Apr 2024 07:52:16 +0000 Subject: [PATCH 04/23] feat(stream): Add function state v2 Add the partitioned stateful concept and refine the function state class. --- libfaasm/core.cpp | 49 ++++++++++++++++++++++++++++++--- libfaasm/faasm/core.h | 23 ++++++++++++++++ libfaasm/faasm/host_interface.h | 16 +++++++++-- libfaasm/libfaasm.imports | 1 + 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index 4780051..70b236e 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -228,17 +228,58 @@ unsigned int getConfFlag(const char* key) return __faasm_conf_flag(key); } +/** + * Creates a new function state + */ +void faasmCreateFunctionState(const unsigned char* data, long dataLen) +{ + __faasm_create_function_state(data, dataLen, nullptr, nullptr); +} + +/** + * Creates a new partitioned function state with its input and state keys. + * It means the it is 'partition stateful function', which can be + * partitioned by the input key, e.g. ID. The stateKey is used to store + * the partitioned state. + */ +void faasmCreatePartitionedFunctionState(const uint8_t* data, + long dataLen, + const char* inputKey, + const char* stateKey) +{ + __faasm_create_function_state(data, dataLen, inputKey, stateKey); +} + +/** + * Write the Function level state into State Storage + */ void faasmWriteFunctionState(const uint8_t* data, long dataLen) { __faasm_write_function_state(data, dataLen); } +/** + * Gets the size of the function state + */ size_t faasmReadFunctionStateSize() { - return __faasm_read_function_state(nullptr, 0); + return __faasm_read_function_state(nullptr, 0, nullptr); } -long faasmReadFunctionState(uint8_t* buffer, long bufferLen) +/** + * Reads the Function levl full state from State Storage + */ +long faasmReadFunctionState(unsigned char* buffer, long bufferLen) { - return __faasm_read_function_state(buffer, bufferLen); -} \ No newline at end of file + return __faasm_read_function_state(buffer, bufferLen, nullptr); +} + +/** + * Read function state. InputKeys is used for partitioned stateful. + */ +long faasmReadParitionedFunctionState(unsigned char* buffer, + long bufferLen, + const char* inputKeys) +{ + return __faasm_read_function_state(buffer, bufferLen, inputKeys); +} diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index 9c3cdaa..2322fcb 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -216,6 +216,22 @@ extern "C" */ void faasmBacktrace(const int depth); + /** + * Creates a new function state + */ + void faasmCreateFunctionState(const unsigned char* data, long dataLen); + + /** + * Creates a new partitioned function state with its input and state keys. + * It means the it is 'partition stateful function', which can be + * partitioned by the input key, e.g. ID. The stateKey is used to store + * the partitioned state. + */ + void faasmCreatePartitionedFunctionState(const unsigned char* data, + long dataLen, + unsigned char* inputKey, + unsigned char* stateKey); + /** * Write the Function level state into State Storage */ @@ -231,6 +247,13 @@ extern "C" */ long faasmReadFunctionState(unsigned char* buffer, long bufferLen); + /** + * Read function state. InputKeys is used for partitioned stateful. + */ + long faasmReadParitionedFunctionState(unsigned char* buffer, + long bufferLen, + unsigned char* inputKeys); + // Macro for defining zygotes (a default fallback noop is provided) int __attribute__((weak)) _faasm_zygote(); #define FAASM_ZYGOTE() int _faasm_zygote() diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index 77f891f..d5b99d7 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -161,9 +161,21 @@ void __faasm_migrate_point(FaasmMigrateEntryPoint f, int arg); HOST_IFACE_FUNC void __faasm_host_interface_test(int testNum); +// Create function also write data to state. If it is partitioned stateful, +// Paritioned Key and State should be passed. +HOST_IFACE_FUNC +void __faasm_create_function_state(const unsigned char* data, + long dataLen, + const char* inputKey, + const char* stateKey); + HOST_IFACE_FUNC void __faasm_write_function_state(const unsigned char* data, long dataLen); +// Read the function state from state server. If it is partitioned stateful, +// paasing the inputKeys will return required the states. HOST_IFACE_FUNC -long __faasm_read_function_state(unsigned char* buffer, long bufferLen); -#endif +long __faasm_read_function_state(unsigned char* buffer, + long bufferLen, + const char* inputKeys); +#endif \ No newline at end of file diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index e6e555a..46e59df 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -46,5 +46,6 @@ __faasm_migrate_point __faasm_host_interface_test # Stream +__faasm_create_function_state __faasm_write_function_state __faasm_read_function_state \ No newline at end of file From a0b9f2ebec2532cc94226f3eeb2cd1a8a793a427 Mon Sep 17 00:00:00 2001 From: Tianyu Date: Fri, 12 Apr 2024 10:55:40 +0000 Subject: [PATCH 05/23] feat(stream) Add serializeParState and serializeMap Add two new functions to the serialization library to serialize --- libfaasm/faasm/serialization.h | 19 ++++- libfaasm/serialization.cpp | 129 ++++++++++++++++++++++++++++----- 2 files changed, 126 insertions(+), 22 deletions(-) diff --git a/libfaasm/faasm/serialization.h b/libfaasm/faasm/serialization.h index 2763d65..81009e6 100644 --- a/libfaasm/faasm/serialization.h +++ b/libfaasm/faasm/serialization.h @@ -4,15 +4,21 @@ #include #include #include +#include + +// THERE MUST BE SAME TO THE FAABRIC SERIALIZATION ! namespace faasm { + +std::size_t hashVector(const std::vector& vec); + // Serialiazion and Deserialization from uint8_t vetors to uint32_t uint32_t uint8VToUint32(const std::vector& bytes); std::vector uint32ToUint8V(uint32_t value); // Serialiazion and Deserialization from uint8_t vetors to Map -std::vector mapToUint8V(const std::map& map); -std::map uint8VToMap( - const std::vector& bytes); +std::vector serializeMapBinary(const std::map& map); +std::map deserializeMapBinary( + const std::vector& buffer); // Serialiazion and Deserialization from uint8_t vetors to Map std::vector mapIntToUint8V(const std::map& map); @@ -23,5 +29,12 @@ std::vector serializeFuncState( const std::map>& map); std::map> deserializeFuncState( const std::vector& bytes); + +// Serialiazion and Deserialization of Paritioned State Input (same as FuncState) +std::vector serializeParState( + const std::map>& map); +std::map> deserializeParState( + const std::vector& bytes); } + #endif \ No newline at end of file diff --git a/libfaasm/serialization.cpp b/libfaasm/serialization.cpp index afc9082..a83abdb 100644 --- a/libfaasm/serialization.cpp +++ b/libfaasm/serialization.cpp @@ -1,6 +1,26 @@ #include "faasm/serialization.h" +#include + namespace faasm { + +// Helper function for combining hash values +inline void hashCombine(std::size_t& seed, std::size_t value) +{ + seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +// Function definition +std::size_t hashVector(const std::vector& vec) +{ + std::size_t hashValue = 0; + for (uint8_t byte : vec) { + std::size_t elementHash = std::hash{}(byte); + hashCombine(hashValue, elementHash); + } + return hashValue; +} + // Transforms a uint8_t vector of bytes into a uint32_t uint32_t uint8VToUint32(const std::vector& bytes) { @@ -21,7 +41,8 @@ std::vector uint32ToUint8V(uint32_t value) } // Transforms a map into a uint8_t vector of bytes. -std::vector mapIntToUint8V(const std::map& map) { +std::vector mapIntToUint8V(const std::map& map) +{ std::vector bytes; for (const auto& pair : map) { std::string key = pair.first; @@ -37,7 +58,8 @@ std::vector mapIntToUint8V(const std::map& map) { } // Transforms a uint8_t vector of bytes into a map . -std::map uint8VToMapInt(const std::vector& bytes) { +std::map uint8VToMapInt(const std::vector& bytes) +{ std::map map; size_t i = 0; while (i < bytes.size()) { @@ -55,16 +77,71 @@ std::map uint8VToMapInt(const std::vector& bytes) { return map; } -void serializeUInt32(std::vector& vec, uint32_t value) { - vec.insert(vec.end(), { - static_cast(value >> 24), - static_cast(value >> 16), - static_cast(value >> 8), - static_cast(value) - }); +std::vector serializeMapBinary(const std::map& map) { + std::vector buffer; + + for (const auto& [key, value] : map) { + // Serialize key size + uint32_t keySize = key.size(); + uint8_t* keySizeBytes = reinterpret_cast(&keySize); + buffer.insert(buffer.end(), keySizeBytes, keySizeBytes + sizeof(keySize)); + + // Serialize key + buffer.insert(buffer.end(), key.begin(), key.end()); + + // Serialize value size + uint32_t valueSize = value.size(); + uint8_t* valueSizeBytes = reinterpret_cast(&valueSize); + buffer.insert(buffer.end(), valueSizeBytes, valueSizeBytes + sizeof(valueSize)); + + // Serialize value + buffer.insert(buffer.end(), value.begin(), value.end()); + } + + return buffer; +} + +std::map deserializeMapBinary(const std::vector& buffer) { + std::map map; + size_t index = 0; + + while (index < buffer.size()) { + // Deserialize key size + uint32_t keySize; + std::copy_n(&buffer[index], sizeof(keySize), reinterpret_cast(&keySize)); + index += sizeof(keySize); + + // Deserialize key + std::string key(&buffer[index], &buffer[index] + keySize); + index += keySize; + + // Deserialize value size + uint32_t valueSize; + std::copy_n(&buffer[index], sizeof(valueSize), reinterpret_cast(&valueSize)); + index += sizeof(valueSize); + + // Deserialize value + std::string value(&buffer[index], &buffer[index] + valueSize); + index += valueSize; + + map[std::move(key)] = std::move(value); + } + + return map; +} + +void serializeUInt32(std::vector& vec, uint32_t value) +{ + vec.insert(vec.end(), + { static_cast(value >> 24), + static_cast(value >> 16), + static_cast(value >> 8), + static_cast(value) }); } -std::vector serializeFuncState(const std::map>& map) { +std::vector serializeFuncState( + const std::map>& map) +{ std::vector serialized; // Pre-calculate required capacity to minimize reallocations size_t totalSize = 0; @@ -83,26 +160,30 @@ std::vector serializeFuncState(const std::map& vec, size_t& index) { - uint32_t value = - (static_cast(vec[index]) << 24) | - (static_cast(vec[index + 1]) << 16) | - (static_cast(vec[index + 2]) << 8) | - (static_cast(vec[index + 3])); +uint32_t deserializeUInt32(const std::vector& vec, size_t& index) +{ + uint32_t value = (static_cast(vec[index]) << 24) | + (static_cast(vec[index + 1]) << 16) | + (static_cast(vec[index + 2]) << 8) | + (static_cast(vec[index + 3])); index += 4; return value; } -std::map> deserializeFuncState(const std::vector& serialized) { +std::map> deserializeFuncState( + const std::vector& serialized) +{ std::map> map; size_t index = 0; while (index < serialized.size()) { auto keyLength = deserializeUInt32(serialized, index); - std::string key(serialized.begin() + index, serialized.begin() + index + keyLength); + std::string key(serialized.begin() + index, + serialized.begin() + index + keyLength); index += keyLength; auto valueLength = deserializeUInt32(serialized, index); - std::vector valueVec(serialized.begin() + index, serialized.begin() + index + valueLength); + std::vector valueVec(serialized.begin() + index, + serialized.begin() + index + valueLength); index += valueLength; map.emplace(std::move(key), std::move(valueVec)); @@ -110,5 +191,15 @@ std::map> deserializeFuncState(const std::vect return map; } +std::vector serializeParState( + const std::map>& map) +{ + return serializeFuncState(map); +} +std::map> deserializeParState( + const std::vector& bytes) +{ + return deserializeFuncState(bytes); +} } \ No newline at end of file From 79422494d396506fb2cca32b1a733e2e9ca6bba0 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Wed, 17 Apr 2024 02:30:54 +0000 Subject: [PATCH 06/23] fix(stream): fix dead lock Adding readlock and writelock functions. --- func/stream/CMakeLists.txt | 2 + func/stream/function_parstate.cpp | 82 +++++++++++++++++++++++ func/stream/function_parstate_ptr.cpp | 84 +++++++++++++++++++++++ func/stream/function_parstate_source.cpp | 29 ++++++++ func/stream/function_state.cpp | 85 +++++++++--------------- func/stream/function_state_ptr.cpp | 54 +++++++++++++++ libfaasm/core.cpp | 51 ++++++++++++-- libfaasm/faasm/core.h | 22 ++++++ libfaasm/faasm/host_interface.h | 20 +++++- 9 files changed, 368 insertions(+), 61 deletions(-) create mode 100644 func/stream/function_parstate.cpp create mode 100644 func/stream/function_parstate_ptr.cpp create mode 100644 func/stream/function_parstate_source.cpp create mode 100644 func/stream/function_state_ptr.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index a22b0ee..5806bc8 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -7,6 +7,8 @@ stream_func(wc_random_source wc_random_source.cpp) stream_func(wc_split wc_split.cpp) stream_func(wc_count wc_count.cpp) stream_func(function_state function_state.cpp) +stream_func(function_parstate function_parstate.cpp) +stream_func(function_parstate_source function_parstate_source.cpp) # Custom target to group all the stream functions add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) diff --git a/func/stream/function_parstate.cpp b/func/stream/function_parstate.cpp new file mode 100644 index 0000000..8e1b897 --- /dev/null +++ b/func/stream/function_parstate.cpp @@ -0,0 +1,82 @@ +#include "faasm/input.h" +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! + +int main(int argc, char* argv[]) +{ + printf("function partition state example\n"); + + const char* inputStr = faasm::getStringInput("noinput"); + // print the input data + printf("function inputStr: %s\n", inputStr); + + size_t readSize = faasmReadFunctionStateSizeLock(); + std::map> functionState; + printf("parstate readSize: %ld\n", readSize); + if (readSize == 0) { + printf("function state is still not initilized, initializing it\n"); + functionState["k1"] = { 1, 2, 3, 4, 5 }; + functionState["k2"] = { 2, 3, 4, 5, 6, 7, 8 }; + functionState["partitionStateKey"] = {}; + } else { + printf("function state is already initialized\n"); + std::vector stateBuffer(readSize); + faasmReadFunctionState(stateBuffer.data(), readSize); + functionState = faasm::deserializeFuncState(stateBuffer); + } + printf("read data finished"); + std::map> parFunctionState; + if (functionState["partitionStateKey"].size() != 0) { + parFunctionState = + faasm::deserializeParState(functionState["partitionStateKey"]); + } + + int count = 0; + if (parFunctionState.find(inputStr) != parFunctionState.end()) { + count = faasm::uint8VToUint32(parFunctionState[inputStr]); + } + count++; + parFunctionState[inputStr] = faasm::uint32ToUint8V(count); + functionState["partitionStateKey"] = + faasm::serializeParState(parFunctionState); + + std::vector functionStateBytes = + faasm::serializeFuncState(functionState); + printf("write back"); + faasmWriteFunctionStateUnlock(functionStateBytes.data(), + functionStateBytes.size()); + + printf("read again, and print the new data."); + // Read it again and compare it. + readSize = faasmReadFunctionStateSizeLock(); + functionState.clear(); + printf("parstate readSize: %ld\n", readSize); + if (readSize == 0) { + printf("error: function state is not written\n"); + faasmFunctionStateUnlock(); + return 1; + } else { + printf("function state is already initialized\n"); + std::vector stateBuffer(readSize); + faasmReadFunctionState(stateBuffer.data(), readSize); + functionState = faasm::deserializeFuncState(stateBuffer); + } + + // Print the function state + for (auto const& x : functionState) { + std::cout << x.first << " : "; + for (auto const& y : x.second) { + std::cout << +y << " "; + } + std::cout << std::endl; + } + faasmFunctionStateUnlock(); + return 0; +} diff --git a/func/stream/function_parstate_ptr.cpp b/func/stream/function_parstate_ptr.cpp new file mode 100644 index 0000000..b5c1daf --- /dev/null +++ b/func/stream/function_parstate_ptr.cpp @@ -0,0 +1,84 @@ +#include "faasm/input.h" +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! + +int main(int argc, char* argv[]) +{ + printf("function partition state example\n"); + + const char* inputStr = faasm::getStringInput("noinput"); + // Read the input from upstream + + printf("inputStr: %s\n", inputStr); + uint8_t* readData = faasmReadFunctionStatePtrLock(); + size_t readSize = 0; + if (readData != nullptr) { + readSize = faasmReadFunctionStateSize(); + } else { + printf("function state is still not initilized, initializing it\n"); + } + printf("parstate readSize: %ld\n", readSize); + std::map> functionState; + if (readSize != 0) { + std::vector buffer(readData, readData + readSize); + printf("buffer is read") ; + functionState = faasm::deserializeFuncState(buffer); + } else { + functionState["k1"] = { 1, 2, 3, 4, 5 }; + functionState["k2"] = { 2, 3, 4, 5, 6, 7, 8 }; + functionState["partitionStateKey"] = {}; + } + printf("read data finished"); + std::map> parFunctionState; + if (functionState["partitionStateKey"].size() != 0) { + parFunctionState = + faasm::deserializeParState(functionState["partitionStateKey"]); + } + + int count = 0; + if (parFunctionState.find(inputStr) != parFunctionState.end()) { + count = faasm::uint8VToUint32(parFunctionState[inputStr]); + } + count++; + parFunctionState[inputStr] = faasm::uint32ToUint8V(count); + functionState["partitionStateKey"] = + faasm::serializeParState(parFunctionState); + + std::vector functionStateBytes = + faasm::serializeFuncState(functionState); + printf("write back"); + faasmWriteFunctionStateUnlock(functionStateBytes.data(), + functionStateBytes.size()); + + printf("read again xxxxxxx "); + + // Read it again and compare it. + readData = faasmReadFunctionStatePtrLock(); + if (readData != nullptr) { + readSize = faasmReadFunctionStateSize(); + } else { + printf("error: function state is not written\n"); + faasmFunctionStateUnlock(); + return 1; + } + std::vector buffer(readData, readData + readSize); + functionState = faasm::deserializeFuncState(buffer); + + // Print the function state + for (auto const& x : functionState) { + std::cout << x.first << " : "; + for (auto const& y : x.second) { + std::cout << +y << " "; + } + std::cout << std::endl; + } + faasmFunctionStateUnlock(); + return 0; +} diff --git a/func/stream/function_parstate_source.cpp b/func/stream/function_parstate_source.cpp new file mode 100644 index 0000000..a8bfad0 --- /dev/null +++ b/func/stream/function_parstate_source.cpp @@ -0,0 +1,29 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/random.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // Create a list of words + std::vector messages = { "cat", "dog", "pig" }; + // Get a random number from 0 to size of messages + int random_number = faasm::randomInteger(0, messages.size() - 1); + // Return a random sentence + std::string message = messages[random_number]; + printf("WordCount source: Created Random sentence: %s\n", message.c_str()); + + std::map> input; + input["partitionInputKey"] = + std::vector(message.begin(), message.end()); + std::vector inputBytes = faasm::serializeParState(input); + printf("output inputyByes: %s\n", inputBytes.data()); + faasmChainNamed("function_parstate", inputBytes.data(), inputBytes.size()); + return 0; +} \ No newline at end of file diff --git a/func/stream/function_state.cpp b/func/stream/function_state.cpp index 5199646..edfc4e3 100644 --- a/func/stream/function_state.cpp +++ b/func/stream/function_state.cpp @@ -6,59 +6,44 @@ #include #include +// We must register the function_state in scheduler! + int main(int argc, char* argv[]) { - // Read a function state - size_t size = faasmReadFunctionStateSize(); - // printf the size - printf("Size of Function State: %ld\n", size); - - // It means it is invoked in first time - // Write a function State - std::vector k1 = { 1, 2, 3, 4, 5 }; - std::vector k2 = { 2, 3, 4, 5, 6, 7, 8 }; - std::vector k3 = { 3, 4, 5, 6, 7, 8 }; - - std::map> functionState = - std::map>(); - functionState["k1"] = k1; - functionState["k2"] = k2; - functionState["k3"] = k3; + printf("function state example\n"); + // Locking the function state at first. + size_t readSize = faasmReadFunctionStateSizeLock(); + std::map> functionState; + if (readSize == 0) { + printf("function state is still not initilized, initializing it\n"); + functionState["k1"] = { 1, 2, 3, 4, 5 }; + functionState["k2"] = { 2, 3, 4, 5, 6, 7, 8 }; + } else { + printf("function state is already initialized\n"); + std::vector stateBuffer(readSize); + faasmReadFunctionState(stateBuffer.data(), readSize); + functionState = faasm::deserializeFuncState(stateBuffer); + } std::vector functionStateBytes = faasm::serializeFuncState(functionState); - faasmWriteFunctionState(functionStateBytes.data(), - functionStateBytes.size()); - - size = faasmReadFunctionStateSize(); - // Read a function state - std::vector buffer(size); - faasmReadFunctionState(buffer.data(), size); - functionState = faasm::deserializeFuncState(buffer); - - // Print the function state - for (auto const& x : functionState) { - std::cout << x.first << " : "; - for (auto const& y : x.second) { - std::cout << +y << " "; - } - std::cout << std::endl; + printf("write function back\n"); + faasmWriteFunctionStateUnlock(functionStateBytes.data(), + functionStateBytes.size()); + + // Read it again and compare it. + readSize = faasmReadFunctionStateSizeLock(); + functionState.clear(); + if (readSize == 0) { + printf("error: function state is not written\n"); + faasmFunctionStateUnlock(); + return 1; + } else { + printf("function state is already initialized\n"); + std::vector stateBuffer(readSize); + faasmReadFunctionState(stateBuffer.data(), readSize); + functionState = faasm::deserializeFuncState(stateBuffer); } - std::vector k4; - k4.reserve(10000); - for (uint16_t i = 1; i <= 10000; ++i) { - k4.push_back(1); - } - functionState["k4"] = k4; - functionStateBytes = faasm::serializeFuncState(functionState); - faasmWriteFunctionState(functionStateBytes.data(), - functionStateBytes.size()); - size = faasmReadFunctionStateSize(); - // Read a function state - std::vector buffer1(size); - faasmReadFunctionState(buffer1.data(), size); - functionState = faasm::deserializeFuncState(buffer1); - // Print the function state for (auto const& x : functionState) { std::cout << x.first << " : "; @@ -67,10 +52,6 @@ int main(int argc, char* argv[]) } std::cout << std::endl; } - - // // size_t size = faasmReadFunctionStateSize(); - // // Cleanup - // delete functionState; - + faasmFunctionStateUnlock(); return 0; } diff --git a/func/stream/function_state_ptr.cpp b/func/stream/function_state_ptr.cpp new file mode 100644 index 0000000..984a870 --- /dev/null +++ b/func/stream/function_state_ptr.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include + + +// We must register the function_state in scheduler! + +int main(int argc, char* argv[]) +{ + printf("function state example\n"); + uint8_t* readData = faasmReadFunctionStatePtrLock(); + size_t readSize = 0; + if (readData != nullptr) { + readSize = faasmReadFunctionStateSize(); + } else { + printf("function state is still not initilized, initializing it\n"); + } + std::map> functionState; + functionState["k1"] = { 1, 2, 3, 4, 5 }; + functionState["k2"] = { 2, 3, 4, 5, 6, 7, 8 }; + + std::vector functionStateBytes = + faasm::serializeFuncState(functionState); + + faasmWriteFunctionStateUnlock(functionStateBytes.data(), + functionStateBytes.size()); + + // Read it again and compare it. + readData = faasmReadFunctionStatePtrLock(); + if (readData != nullptr) { + readSize = faasmReadFunctionStateSize(); + } else { + printf("error: function state is not written\n"); + faasmFunctionStateUnlock(); + return 1; + } + std::vector buffer(readData, readData + readSize); + functionState = faasm::deserializeFuncState(buffer); + + // Print the function state + for (auto const& x : functionState) { + std::cout << x.first << " : "; + for (auto const& y : x.second) { + std::cout << +y << " "; + } + std::cout << std::endl; + } + faasmFunctionStateUnlock(); + return 0; +} diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index 70b236e..fe60dd2 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -263,7 +263,15 @@ void faasmWriteFunctionState(const uint8_t* data, long dataLen) */ size_t faasmReadFunctionStateSize() { - return __faasm_read_function_state(nullptr, 0, nullptr); + return __faasm_read_function_state_size(0); +} + +/** + * Gets the size of the function state + */ +size_t faasmReadFunctionStateSizeLock() +{ + return __faasm_read_function_state_size(1); } /** @@ -271,15 +279,44 @@ size_t faasmReadFunctionStateSize() */ long faasmReadFunctionState(unsigned char* buffer, long bufferLen) { - return __faasm_read_function_state(buffer, bufferLen, nullptr); + return __faasm_read_function_state(buffer, bufferLen); } +// /** +// * Read function state. InputKeys is used for partitioned stateful. +// */ +// long faasmReadParitionedFunctionState(unsigned char* buffer, +// long bufferLen, +// const char* inputKeys) +// { +// return __faasm_read_function_state(buffer, bufferLen, inputKeys); +// } + /** - * Read function state. InputKeys is used for partitioned stateful. + * Read function state data. It returns a pointer of vector. If + * nullptr is returned, means this state is created but not initialized. In + * the same time the data is locked. */ -long faasmReadParitionedFunctionState(unsigned char* buffer, - long bufferLen, - const char* inputKeys) +uint8_t* faasmReadFunctionStatePtrLock() { - return __faasm_read_function_state(buffer, bufferLen, inputKeys); + return __faasm_read_function_state_ptr_lock(); } + +/** + * Write the function state into state server. This function won't create + * any functionstate object. It will also unlock the function after writing. + */ +void faasmWriteFunctionStateUnlock(const uint8_t* data, long dataLen) +{ + __faasm_write_function_state_unlock(data, dataLen); +} + +long faasmFunctionStateLock() +{ + return __faasm_function_state_lock(); +} + +void faasmFunctionStateUnlock() +{ + __faasm_function_state_unlock(); +} \ No newline at end of file diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index 2322fcb..7d8b4cb 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -242,6 +242,11 @@ extern "C" */ size_t faasmReadFunctionStateSize(); + /** + * Gets the size of the function state + */ + size_t faasmReadFunctionStateSizeLock(); + /** * Reads the Function levl full state from State Storage */ @@ -254,6 +259,23 @@ extern "C" long bufferLen, unsigned char* inputKeys); + /** + * Read function state data. It returns a pointer of vector. If + * nullptr is returned, means this state is created but not initialized. In + * the same time the data is locked. + */ + uint8_t* faasmReadFunctionStatePtrLock(); + + /** + * Write the function state into state server. This function won't create + * any functionstate object. It will also unlock the function after writing. + */ + void faasmWriteFunctionStateUnlock(const uint8_t* data, long dataLen); + + long faasmFunctionStateLock(); + + void faasmFunctionStateUnlock(); + // Macro for defining zygotes (a default fallback noop is provided) int __attribute__((weak)) _faasm_zygote(); #define FAASM_ZYGOTE() int _faasm_zygote() diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index d5b99d7..a0ef667 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -176,6 +176,22 @@ void __faasm_write_function_state(const unsigned char* data, long dataLen); // paasing the inputKeys will return required the states. HOST_IFACE_FUNC long __faasm_read_function_state(unsigned char* buffer, - long bufferLen, - const char* inputKeys); + long bufferLen); + +HOST_IFACE_FUNC +unsigned char* __faasm_read_function_state_ptr_lock(); + +HOST_IFACE_FUNC +void __faasm_write_function_state_unlock(const unsigned char* data, + long dataLen); + +HOST_IFACE_FUNC +long __faasm_function_state_lock(); + +HOST_IFACE_FUNC +void __faasm_function_state_unlock(); + +// lock == 0 means false, lock == 1 means true. +HOST_IFACE_FUNC +long __faasm_read_function_state_size(int lock); #endif \ No newline at end of file From b1322b9d7dcf1d82e9e47520c2dec9e5fe9cbeb0 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Fri, 26 Apr 2024 03:22:52 +0000 Subject: [PATCH 07/23] feat(libfaasm): Allowing Stream Batch-Processing New serialization for input and pass idx to intrinsic function. --- func/stream/CMakeLists.txt | 1 + func/stream/batch_echo.cpp | 36 +++++++ func/stream/function_parstate.cpp | 68 +++++++++++-- func/stream/function_parstate_source.cpp | 36 ++++--- libfaasm/core.cpp | 12 ++- libfaasm/faasm/core.h | 8 ++ libfaasm/faasm/host_interface.h | 3 +- libfaasm/faasm/input.h | 3 + libfaasm/faasm/serialization.h | 37 ++++++- libfaasm/input.cpp | 13 +++ libfaasm/libfaasm.imports | 7 +- libfaasm/serialization.cpp | 121 +++++++++++++++++++++-- 12 files changed, 312 insertions(+), 33 deletions(-) create mode 100644 func/stream/batch_echo.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index 5806bc8..c97be64 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -9,6 +9,7 @@ stream_func(wc_count wc_count.cpp) stream_func(function_state function_state.cpp) stream_func(function_parstate function_parstate.cpp) stream_func(function_parstate_source function_parstate_source.cpp) +stream_func(batch_echo batch_echo.cpp) # Custom target to group all the stream functions add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) diff --git a/func/stream/batch_echo.cpp b/func/stream/batch_echo.cpp new file mode 100644 index 0000000..203e39e --- /dev/null +++ b/func/stream/batch_echo.cpp @@ -0,0 +1,36 @@ +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/serialization.h" + +#include +#include +#include +#include +#include + +/** + * Writes the input to the output + */ +int main(int argc, char* argv[]) +{ + std::vector vec = faasm::getInputVec(); + size_t inputLen = vec.size(); + + // Handle empty input + if (inputLen == 0) { + const char* output = "Nothing to echo"; + faasmSetOutput(output, strlen(output)); + return 0; + } + + std::map inputMap = + faasm::deserializeMapBinary(vec); + // printf the inputMap + for (const auto& pair : inputMap) { + printf("Key: %s Value: %s\n", pair.first.c_str(), pair.second.c_str()); + } + + // faasmSetOutput(inputStr, inputLen); + + return 0; +} diff --git a/func/stream/function_parstate.cpp b/func/stream/function_parstate.cpp index 8e1b897..89c7f74 100644 --- a/func/stream/function_parstate.cpp +++ b/func/stream/function_parstate.cpp @@ -13,10 +13,27 @@ int main(int argc, char* argv[]) { printf("function partition state example\n"); - const char* inputStr = faasm::getStringInput("noinput"); - // print the input data - printf("function inputStr: %s\n", inputStr); + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + // printf the vector + printf("input data: "); + for (const auto& i : vec) { + printf("%d ", i); + } + printf("\n read data finished. \n"); + size_t index = 0; // Reset index if reusing buffer + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + // printf the inputMap (inputdata) + for (const auto& pair : inputMap) { + printf("Key: %s Value: ", pair.first.c_str()); + for (const auto& innerPair : pair.second) { + printf("%s %s ", innerPair.first.c_str(), innerPair.second.c_str()); + } + printf("\n"); + } + // get the functionstate size_t readSize = faasmReadFunctionStateSizeLock(); std::map> functionState; printf("parstate readSize: %ld\n", readSize); @@ -31,22 +48,40 @@ int main(int argc, char* argv[]) faasmReadFunctionState(stateBuffer.data(), readSize); functionState = faasm::deserializeFuncState(stateBuffer); } - printf("read data finished"); + printf("read data finished. \n"); + + // get the partitionstate from functionstate std::map> parFunctionState; if (functionState["partitionStateKey"].size() != 0) { parFunctionState = faasm::deserializeParState(functionState["partitionStateKey"]); } - int count = 0; - if (parFunctionState.find(inputStr) != parFunctionState.end()) { - count = faasm::uint8VToUint32(parFunctionState[inputStr]); + /* + Begin the loop + */ + + for (size_t i = 0; i < inputMap.size(); i++) { + // get the input for this spefic function invoke. + std::string inputParStr = inputMap[std::to_string(i)]["partitionInputKey"]; + + // print the input par + printf("the ith: %zu is inputParStr: %s\n", i, inputParStr.c_str()); + // increament the count + int count = 0; + if (parFunctionState.find(inputParStr) != parFunctionState.end()) { + count = faasm::uint8VToUint32(parFunctionState[inputParStr]); + } + count++; + parFunctionState[inputParStr] = faasm::uint32ToUint8V(count); } - count++; - parFunctionState[inputStr] = faasm::uint32ToUint8V(count); + + /* + After the loop + */ functionState["partitionStateKey"] = faasm::serializeParState(parFunctionState); - + // write data back std::vector functionStateBytes = faasm::serializeFuncState(functionState); printf("write back"); @@ -70,6 +105,7 @@ int main(int argc, char* argv[]) } // Print the function state + printf("function state: \n"); for (auto const& x : functionState) { std::cout << x.first << " : "; for (auto const& y : x.second) { @@ -77,6 +113,18 @@ int main(int argc, char* argv[]) } std::cout << std::endl; } + printf("partition state \n"); + // Print the partition state + parFunctionState = + faasm::deserializeParState(functionState["partitionStateKey"]); + for (auto const& x : parFunctionState) { + std::cout << x.first << " : "; + for (auto const& y : x.second) { + std::cout << +y << " "; + } + std::cout << std::endl; + } + faasmFunctionStateUnlock(); return 0; } diff --git a/func/stream/function_parstate_source.cpp b/func/stream/function_parstate_source.cpp index a8bfad0..81ae9ed 100644 --- a/func/stream/function_parstate_source.cpp +++ b/func/stream/function_parstate_source.cpp @@ -1,5 +1,6 @@ #include "faasm/core.h" #include "faasm/faasm.h" +#include "faasm/input.h" #include "faasm/random.h" #include #include @@ -11,19 +12,32 @@ int main(int argc, char* argv[]) { + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec,index); + // printf the inputMap size + printf("inputMap size: %ld\n", inputMap.size()); // Create a list of words std::vector messages = { "cat", "dog", "pig" }; - // Get a random number from 0 to size of messages - int random_number = faasm::randomInteger(0, messages.size() - 1); - // Return a random sentence - std::string message = messages[random_number]; - printf("WordCount source: Created Random sentence: %s\n", message.c_str()); - std::map> input; - input["partitionInputKey"] = - std::vector(message.begin(), message.end()); - std::vector inputBytes = faasm::serializeParState(input); - printf("output inputyByes: %s\n", inputBytes.data()); - faasmChainNamed("function_parstate", inputBytes.data(), inputBytes.size()); + for (int i = 0; i < inputMap.size(); i++) { + // Get a random number from 0 to size of messages + int random_number = faasm::randomInteger(0, messages.size() - 1); + // Return a random sentence + std::string message = messages[random_number]; + printf("WordCount source: Created Random sentence: %s\n", + message.c_str()); + + std::map input; + input["partitionInputKey"] = message; + input["key2"] = "key2value"; + std::vector inputBytes; + faasm::serializeMap(inputBytes, input); + + faasmChainNamedId( + "function_parstate", inputBytes.data(), inputBytes.size(), i); + } return 0; } \ No newline at end of file diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index fe60dd2..188b7ae 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -162,7 +162,17 @@ unsigned int faasmChainNamed(const char* name, const uint8_t* inputData, long inputDataSize) { - return __faasm_chain_name(name, inputData, inputDataSize); + return __faasm_chain_name(name, inputData, inputDataSize, 0); +} + +// This function is desiged for batch processing, the message has to call the +// with idx +unsigned int faasmChainNamedId(const char* name, + const uint8_t* inputData, + long inputDataSize, + int idx) +{ + return __faasm_chain_name(name, inputData, inputDataSize, idx); } unsigned int faasmChain(FaasmFuncPtr funcPtr, diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index 7d8b4cb..64a3bda 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -164,6 +164,14 @@ extern "C" const uint8_t* inputData, long inputDataSize); + /** + * Chains a function with the given input data with the current messageIdx + */ + unsigned int faasmChainNamedId(const char* name, + const uint8_t* inputData, + long inputDataSize, + int idx); + /** * Chains a function from this module with the given input data */ diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index a0ef667..0586607 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -102,7 +102,8 @@ void __faasm_write_output(const char* output, long outputLen); HOST_IFACE_FUNC unsigned int __faasm_chain_name(const char* name, const unsigned char* inputData, - long inputDataSize); + long inputDataSize, + int idx); HOST_IFACE_FUNC unsigned int __faasm_chain_ptr(int (*funcPtr)(), diff --git a/libfaasm/faasm/input.h b/libfaasm/faasm/input.h index 36dd486..b379e12 100644 --- a/libfaasm/faasm/input.h +++ b/libfaasm/faasm/input.h @@ -2,10 +2,13 @@ #define FAASM_INPUT_H #include "faasm/core.h" +#include namespace faasm { const char* getStringInput(const char* defaultValue); +const std::vector getInputVec(); + void setStringOutput(const char* val); int getIntInput(); diff --git a/libfaasm/faasm/serialization.h b/libfaasm/faasm/serialization.h index 81009e6..745bd92 100644 --- a/libfaasm/faasm/serialization.h +++ b/libfaasm/faasm/serialization.h @@ -1,10 +1,10 @@ #ifndef FAASM_SERIALIZATION_H #define FAASM_SERIALIZATION_H +#include #include #include #include #include -#include // THERE MUST BE SAME TO THE FAABRIC SERIALIZATION ! namespace faasm { @@ -16,7 +16,8 @@ uint32_t uint8VToUint32(const std::vector& bytes); std::vector uint32ToUint8V(uint32_t value); // Serialiazion and Deserialization from uint8_t vetors to Map -std::vector serializeMapBinary(const std::map& map); +std::vector serializeMapBinary( + const std::map& map); std::map deserializeMapBinary( const std::vector& buffer); @@ -30,11 +31,41 @@ std::vector serializeFuncState( std::map> deserializeFuncState( const std::vector& bytes); -// Serialiazion and Deserialization of Paritioned State Input (same as FuncState) +// Serialiazion and Deserialization of Paritioned State Input (same as +// FuncState) std::vector serializeParState( const std::map>& map); std::map> deserializeParState( const std::vector& bytes); + +// Serialize a string into a vector of uint8_t +void serializeString(std::vector& buffer, + const std::string& str); + +// Deserialize a string from a vector of uint8_t +std::string deserializeString(const std::vector& buffer, + size_t& index); + +// Serialize a map of strings +void serializeMap(std::vector& buffer, + const std::map& map); + +// Deserialize a map of strings +std::map deserializeMap( + const std::vector& buffer, + size_t& index); + +// Serialize a nested map +void serializeNestedMap( + std::vector& buffer, + const std::map>& nestedMap); + +// Deserialize a nested map +std::map> +deserializeNestedMap(const std::vector& buffer, size_t& index); + +// Helper function to append an unsigned 32-bit integer to the buffer +void appendUint32(std::vector& buffer, uint32_t value); } #endif \ No newline at end of file diff --git a/libfaasm/input.cpp b/libfaasm/input.cpp index 8ba2191..e0b7724 100644 --- a/libfaasm/input.cpp +++ b/libfaasm/input.cpp @@ -24,6 +24,19 @@ const char* getStringInput(const char* defaultValue) return strIn; } +// We use Vec here since, sometimes input cannot cast to string +const std::vector getInputVec(){ + long inputSize = faasmGetInputSize(); + if (inputSize == 0) { + return std::vector(); + } + std::vector inputBuffer(inputSize); + faasmGetInput(inputBuffer.data(), inputSize); + + // Convert to string if returning is necessary + return inputBuffer; +} + int getIntInput() { const char* inputStr = faasm::getStringInput("0"); diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index 46e59df..48f9181 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -48,4 +48,9 @@ __faasm_host_interface_test # Stream __faasm_create_function_state __faasm_write_function_state -__faasm_read_function_state \ No newline at end of file +__faasm_read_function_state +__faasm_read_function_state_ptr_lock +__faasm_write_function_state_unlock +__faasm_function_state_lock +__faasm_function_state_unlock +__faasm_read_function_state_size \ No newline at end of file diff --git a/libfaasm/serialization.cpp b/libfaasm/serialization.cpp index a83abdb..fbb2255 100644 --- a/libfaasm/serialization.cpp +++ b/libfaasm/serialization.cpp @@ -1,6 +1,8 @@ #include "faasm/serialization.h" #include +#include // For memcpy +#include // For std::setw and std::setfill namespace faasm { @@ -77,14 +79,17 @@ std::map uint8VToMapInt(const std::vector& bytes) return map; } -std::vector serializeMapBinary(const std::map& map) { +std::vector serializeMapBinary( + const std::map& map) +{ std::vector buffer; for (const auto& [key, value] : map) { // Serialize key size uint32_t keySize = key.size(); uint8_t* keySizeBytes = reinterpret_cast(&keySize); - buffer.insert(buffer.end(), keySizeBytes, keySizeBytes + sizeof(keySize)); + buffer.insert( + buffer.end(), keySizeBytes, keySizeBytes + sizeof(keySize)); // Serialize key buffer.insert(buffer.end(), key.begin(), key.end()); @@ -92,7 +97,8 @@ std::vector serializeMapBinary(const std::map // Serialize value size uint32_t valueSize = value.size(); uint8_t* valueSizeBytes = reinterpret_cast(&valueSize); - buffer.insert(buffer.end(), valueSizeBytes, valueSizeBytes + sizeof(valueSize)); + buffer.insert( + buffer.end(), valueSizeBytes, valueSizeBytes + sizeof(valueSize)); // Serialize value buffer.insert(buffer.end(), value.begin(), value.end()); @@ -101,14 +107,18 @@ std::vector serializeMapBinary(const std::map return buffer; } -std::map deserializeMapBinary(const std::vector& buffer) { +std::map deserializeMapBinary( + const std::vector& buffer) +{ std::map map; size_t index = 0; while (index < buffer.size()) { // Deserialize key size uint32_t keySize; - std::copy_n(&buffer[index], sizeof(keySize), reinterpret_cast(&keySize)); + std::copy_n(&buffer[index], + sizeof(keySize), + reinterpret_cast(&keySize)); index += sizeof(keySize); // Deserialize key @@ -117,7 +127,9 @@ std::map deserializeMapBinary(const std::vector(&valueSize)); + std::copy_n(&buffer[index], + sizeof(valueSize), + reinterpret_cast(&valueSize)); index += sizeof(valueSize); // Deserialize value @@ -202,4 +214,101 @@ std::map> deserializeParState( return deserializeFuncState(bytes); } +// Helper function to append an unsigned 32-bit integer to the buffer +void appendUint32(std::vector& buffer, uint32_t value) +{ + uint8_t temp[4]; + std::memcpy(temp, &value, 4); + buffer.insert(buffer.end(), temp, temp + 4); +} + +// Serialize a string into a vector of uint8_t +void serializeString(std::vector& buffer, const std::string& str) +{ + appendUint32(buffer, static_cast(str.size())); // Length of string + buffer.insert(buffer.end(), str.begin(), str.end()); // String characters +} + +// Deserialize a string from a vector of uint8_t +std::string deserializeString(const std::vector& buffer, size_t& index) +{ + uint32_t length = *reinterpret_cast(&buffer[index]); + index += 4; + std::string str(buffer.begin() + index, buffer.begin() + index + length); + index += length; + return str; +} + +// Serialize a map of strings +void serializeMap(std::vector& buffer, + const std::map& map) +{ + appendUint32(buffer, static_cast(map.size())); // Number of pairs + for (const auto& pair : map) { + serializeString(buffer, pair.first); // Serialize key + serializeString(buffer, pair.second); // Serialize value + } +} + +// Deserialize a map of strings +std::map deserializeMap( + const std::vector& buffer, + size_t& index) +{ + // Handle empty buffer or index out of range + if (buffer.size() < index + 4) { + return {}; // Return an empty map if there's not enough data + } + + uint32_t numPairs = *reinterpret_cast(&buffer[index]); + index += 4; + std::map map; + + for (uint32_t i = 0; i < numPairs; ++i) { + if (buffer.size() < index + 1) { + throw std::runtime_error("Buffer too small for expected number of pairs"); + } + std::string key = deserializeString(buffer, index); + std::string value = deserializeString(buffer, index); + map[std::move(key)] = std::move(value); + } + return map; +} + +// Serialize a nested map +void serializeNestedMap( + std::vector& buffer, + const std::map>& nestedMap) +{ + appendUint32(buffer, static_cast(nestedMap.size())); // Number of outer pairs + for (const auto& pair : nestedMap) { + serializeString(buffer, pair.first); // Serialize outer key + serializeMap(buffer, pair.second); // Serialize inner map + } +} + +// Deserialize a nested map +std::map> deserializeNestedMap( + const std::vector& buffer, + size_t& index) +{ + if (buffer.size() < index + 4) { + return {}; // Return an empty nested map if there's not enough data + } + + uint32_t numPairs = *reinterpret_cast(&buffer[index]); + index += 4; + std::map> nestedMap; + + for (uint32_t i = 0; i < numPairs; ++i) { + if (buffer.size() < index + 1) { + throw std::runtime_error("Buffer too small for expected number of outer pairs"); + } + std::string key = deserializeString(buffer, index); + std::map valueMap = deserializeMap(buffer, index); + nestedMap[std::move(key)] = std::move(valueMap); + } + return nestedMap; +} + } \ No newline at end of file From e96dfdf4ef307e5df5f63ae2e03ac7357b57f81f Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Sat, 11 May 2024 05:01:34 +0000 Subject: [PATCH 08/23] test(chain): Add chained example It is used for metric testing. --- func/stream/CMakeLists.txt | 4 ++++ func/stream/chained_a.cpp | 41 ++++++++++++++++++++++++++++++++++++++ func/stream/chained_b.cpp | 38 +++++++++++++++++++++++++++++++++++ func/stream/chained_c.cpp | 30 ++++++++++++++++++++++++++++ func/stream/chained_d.cpp | 30 ++++++++++++++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 func/stream/chained_a.cpp create mode 100644 func/stream/chained_b.cpp create mode 100644 func/stream/chained_c.cpp create mode 100644 func/stream/chained_d.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index c97be64..48f3b45 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -3,6 +3,10 @@ function(stream_func exec_name dir_path) faasm_func(${exec_name} ${dir_path}) set(ALL_STREAM_FUNCS ${ALL_STREAM_FUNCS} ${exec_name} PARENT_SCOPE) endfunction(stream_func) +stream_func(chained_a chained_a.cpp) +stream_func(chained_b chained_b.cpp) +stream_func(chained_c chained_c.cpp) +stream_func(chained_d chained_d.cpp) stream_func(wc_random_source wc_random_source.cpp) stream_func(wc_split wc_split.cpp) stream_func(wc_count wc_count.cpp) diff --git a/func/stream/chained_a.cpp b/func/stream/chained_a.cpp new file mode 100644 index 0000000..2444749 --- /dev/null +++ b/func/stream/chained_a.cpp @@ -0,0 +1,41 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/random.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + // printf the inputMap size + printf("chained_a- inputMap size: %ld\n", inputMap.size()); + // Create a list of words + std::vector messages = { "cat", "dog", "pig" }; + + for (int i = 0; i < inputMap.size(); i++) { + // Get a random number from 0 to size of messages + int random_number = faasm::randomInteger(0, messages.size() - 1); + // Return a random sentence + std::string message = messages[random_number]; + printf("chained_a- Created Random sentence: %s\n", message.c_str()); + + std::map input; + input["key1"] = message; + input["key2"] = "key2value"; + std::vector inputBytes; + faasm::serializeMap(inputBytes, input); + + faasmChainNamedId("chained_b", inputBytes.data(), inputBytes.size(), i); + } + return 0; +} \ No newline at end of file diff --git a/func/stream/chained_b.cpp b/func/stream/chained_b.cpp new file mode 100644 index 0000000..02f7c53 --- /dev/null +++ b/func/stream/chained_b.cpp @@ -0,0 +1,38 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/random.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + // printf the inputMap size + printf("chained_b- inputMap size: %ld\n", inputMap.size()); + + for (int i = 0; i < inputMap.size(); i++) { + // Return a random sentence + std::string message = inputMap[std::to_string(i)]["key1"]; + printf("chained_b- the input is: %s\n", message.c_str()); + + std::map input; + input["key1"] = message; + input["key2"] = "key2value"; + std::vector inputBytes; + faasm::serializeMap(inputBytes, input); + + faasmChainNamedId("chained_c", inputBytes.data(), inputBytes.size(), i); + faasmChainNamedId("chained_d", inputBytes.data(), inputBytes.size(), i); + } + return 0; +} \ No newline at end of file diff --git a/func/stream/chained_c.cpp b/func/stream/chained_c.cpp new file mode 100644 index 0000000..be9307b --- /dev/null +++ b/func/stream/chained_c.cpp @@ -0,0 +1,30 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/random.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + // printf the inputMap size + printf("chained_c- inputMap size: %ld\n", inputMap.size()); + // Create a list of words + + for (int i = 0; i < inputMap.size(); i++) { + // Get a random number from 0 to size of messages + std::string message = inputMap[std::to_string(i)]["key1"]; + printf("chained_c- the input is: %s\n", message.c_str()); + } + return 0; +} \ No newline at end of file diff --git a/func/stream/chained_d.cpp b/func/stream/chained_d.cpp new file mode 100644 index 0000000..2875d1f --- /dev/null +++ b/func/stream/chained_d.cpp @@ -0,0 +1,30 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/random.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + // printf the inputMap size + printf("chained_d- inputMap size: %ld\n", inputMap.size()); + // Create a list of words + + for (int i = 0; i < inputMap.size(); i++) { + // Get a random number from 0 to size of messages + std::string message = inputMap[std::to_string(i)]["key1"]; + printf("chained_d- the input is: %s\n", message.c_str()); + } + return 0; +} \ No newline at end of file From 20228c8d072ee1d69efbc580ca415e39b8b619f4 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Tue, 14 May 2024 04:00:21 +0000 Subject: [PATCH 09/23] feat(requirements): Changes FaasmCtl to MyfaasmCtl As title --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 013f621..f0c6031 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ black==22.3.0 -faasmctl==0.13.0 +# faasmctl==0.13.0 +myfaasmctl==0.0.2 flake8==4.0.1 invoke>=2.0.0 requests>=2.31.0 From c682488842b02b00691b3b7e130a8f265c357153 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Mon, 20 May 2024 10:03:32 +0000 Subject: [PATCH 10/23] feat(planner, executor): Support chain and set result in batch All the chained messages will be stored in queue, user has to invoke it. --- func/stream/function_parstate_source.cpp | 2 ++ libfaasm/core.cpp | 11 ++++++++--- libfaasm/faasm/core.h | 2 ++ libfaasm/faasm/host_interface.h | 3 +++ libfaasm/libfaasm.imports | 3 ++- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/func/stream/function_parstate_source.cpp b/func/stream/function_parstate_source.cpp index 81ae9ed..a0c84d1 100644 --- a/func/stream/function_parstate_source.cpp +++ b/func/stream/function_parstate_source.cpp @@ -39,5 +39,7 @@ int main(int argc, char* argv[]) faasmChainNamedId( "function_parstate", inputBytes.data(), inputBytes.size(), i); } + + faasmChainInvoke(); return 0; } \ No newline at end of file diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index 188b7ae..1ca2a82 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -168,9 +168,9 @@ unsigned int faasmChainNamed(const char* name, // This function is desiged for batch processing, the message has to call the // with idx unsigned int faasmChainNamedId(const char* name, - const uint8_t* inputData, - long inputDataSize, - int idx) + const uint8_t* inputData, + long inputDataSize, + int idx) { return __faasm_chain_name(name, inputData, inputDataSize, idx); } @@ -329,4 +329,9 @@ long faasmFunctionStateLock() void faasmFunctionStateUnlock() { __faasm_function_state_unlock(); +} + +void faasmChainInvoke() +{ + __faasm_chain_invoke(); } \ No newline at end of file diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index 64a3bda..763a1c0 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -284,6 +284,8 @@ extern "C" void faasmFunctionStateUnlock(); + void faasmChainInvoke(); + // Macro for defining zygotes (a default fallback noop is provided) int __attribute__((weak)) _faasm_zygote(); #define FAASM_ZYGOTE() int _faasm_zygote() diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index 0586607..26cc2bf 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -195,4 +195,7 @@ void __faasm_function_state_unlock(); // lock == 0 means false, lock == 1 means true. HOST_IFACE_FUNC long __faasm_read_function_state_size(int lock); + +HOST_IFACE_FUNC +void __faasm_chain_invoke(); #endif \ No newline at end of file diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index 48f9181..f3222be 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -53,4 +53,5 @@ __faasm_read_function_state_ptr_lock __faasm_write_function_state_unlock __faasm_function_state_lock __faasm_function_state_unlock -__faasm_read_function_state_size \ No newline at end of file +__faasm_read_function_state_size +__faasm_chain_invoke \ No newline at end of file From 37c6bb7d2919d32ecef36ca2253e52364ce08641 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Sun, 26 May 2024 07:30:10 +0000 Subject: [PATCH 11/23] feat(stream): Add wordcount example As title --- func/stream/CMakeLists.txt | 3 ++ func/stream/wordcount_count.cpp | 74 ++++++++++++++++++++++++++++++++ func/stream/wordcount_source.cpp | 51 ++++++++++++++++++++++ func/stream/wordcount_split.cpp | 64 +++++++++++++++++++++++++++ requirements.txt | 2 +- 5 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 func/stream/wordcount_count.cpp create mode 100644 func/stream/wordcount_source.cpp create mode 100644 func/stream/wordcount_split.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index 48f3b45..b9d237d 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -14,6 +14,9 @@ stream_func(function_state function_state.cpp) stream_func(function_parstate function_parstate.cpp) stream_func(function_parstate_source function_parstate_source.cpp) stream_func(batch_echo batch_echo.cpp) +stream_func(wordcount_source wordcount_source.cpp) +stream_func(wordcount_split wordcount_split.cpp) +stream_func(wordcount_count wordcount_count.cpp) # Custom target to group all the stream functions add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) diff --git a/func/stream/wordcount_count.cpp b/func/stream/wordcount_count.cpp new file mode 100644 index 0000000..bc11f42 --- /dev/null +++ b/func/stream/wordcount_count.cpp @@ -0,0 +1,74 @@ +#include "faasm/input.h" +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + + size_t index = 0; // Reset index if reusing buffer + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + + // get the functionstate + size_t readSize = faasmReadFunctionStateSizeLock(); + std::map> functionState; + if (readSize == 0) { + functionState["partitionStateKey"] = {}; + } else { + std::vector stateBuffer(readSize); + faasmReadFunctionState(stateBuffer.data(), readSize); + functionState = faasm::deserializeFuncState(stateBuffer); + } + + // get the partitionstate from functionstate + std::map> parFunctionState; + if (functionState["partitionStateKey"].size() != 0) { + parFunctionState = + faasm::deserializeParState(functionState["partitionStateKey"]); + } + + /* + Begin the loop + */ + + for (size_t i = 0; i < inputMap.size(); i++) { + // get the input for this spefic function invoke. + std::string inputParStr = inputMap[std::to_string(i)]["partitionInputKey"]; + + // increament the count + int count = 0; + if (parFunctionState.find(inputParStr) != parFunctionState.end()) { + count = faasm::uint8VToUint32(parFunctionState[inputParStr]); + } + count++; + parFunctionState[inputParStr] = faasm::uint32ToUint8V(count); + } + + // Print the parFunctionState + std::cout << "Printing the partitioned function state" << std::endl; + for (auto const& x : parFunctionState) { + std::cout << x.first << " : " << faasm::uint8VToUint32(x.second) << std::endl; + } + + /* + After the loop + */ + functionState["partitionStateKey"] = + faasm::serializeParState(parFunctionState); + // write data back + std::vector functionStateBytes = + faasm::serializeFuncState(functionState); + faasmWriteFunctionStateUnlock(functionStateBytes.data(), + functionStateBytes.size()); + + return 0; +} diff --git a/func/stream/wordcount_source.cpp b/func/stream/wordcount_source.cpp new file mode 100644 index 0000000..b812cc7 --- /dev/null +++ b/func/stream/wordcount_source.cpp @@ -0,0 +1,51 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/random.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + + // Create a list of words + std::vector messages = { + "the cow jumped over the moon", + "an apple a day keeps the doctor away", + "four score and seven years ago", + "snow white and the seven dwarfs", + "i am at two with nature" + }; + + // Iterate over the input data and create a random sentence for each + for (int i = 0; i < inputMap.size(); i++) { + // Get a random number from 0 to size of messages + int random_number = faasm::randomInteger(0, messages.size() - 1); + // Return a random sentence + std::string message = messages[random_number]; + + // Prepare for chain call + std::map chainedInput; + chainedInput["sentence"] = message; + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + // Chain call next function. + faasmChainNamedId("wordcount_split", + chainedInputBytes.data(), + chainedInputBytes.size(), + i); + } + + faasmChainInvoke(); + return 0; +} \ No newline at end of file diff --git a/func/stream/wordcount_split.cpp b/func/stream/wordcount_split.cpp new file mode 100644 index 0000000..d89a91a --- /dev/null +++ b/func/stream/wordcount_split.cpp @@ -0,0 +1,64 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + + // Iterate over the input data and split each sentence + for (int i = 0; i < inputMap.size(); i++) { + // Split the inputSentence by space and store in a vector. Then chained + // call next function. + std::string inputSentence = inputMap[std::to_string(i)]["sentence"]; + // Print the inputSentence + printf("WordCount split: Received sentence: %s\n", inputSentence.c_str()); + + std::string word; + + for (char x : inputSentence) { + if (x == ' ') { + if (!word.empty()) { + // Prepare for chain call + std::map chainedInput; + chainedInput["partitionInputKey"] = word; + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + faasmChainNamedId("wordcount_count", + chainedInputBytes.data(), + chainedInputBytes.size(), + i); + word.clear(); + } + } else { + word = word + x; + } + } + if (!word.empty()) { + // Prepare for chain call + std::map chainedInput; + chainedInput["partitionInputKey"] = word; + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + + faasmChainNamedId("wordcount_count", + chainedInputBytes.data(), + chainedInputBytes.size(), + i); + } + } + + faasmChainInvoke(); + return 0; +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f0c6031..cd46eb4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ black==22.3.0 # faasmctl==0.13.0 -myfaasmctl==0.0.2 +myfaasmctl==0.0.4 flake8==4.0.1 invoke>=2.0.0 requests>=2.31.0 From ac2eca890ddd4381602f52064b38a950220948a3 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Wed, 29 May 2024 21:27:16 +0000 Subject: [PATCH 12/23] fix(stream): Refine the wordcount example Remove print in wordcount and also update the serialization. --- func/stream/function_parstate.cpp | 15 +++-- func/stream/function_parstate_source.cpp | 18 +++-- func/stream/wordcount_count.cpp | 8 +-- func/stream/wordcount_source.cpp | 14 +++- func/stream/wordcount_split.cpp | 2 +- libfaasm/serialization.cpp | 85 +++++++++++++++++++----- requirements.txt | 2 +- 7 files changed, 109 insertions(+), 35 deletions(-) diff --git a/func/stream/function_parstate.cpp b/func/stream/function_parstate.cpp index 89c7f74..7a9215f 100644 --- a/func/stream/function_parstate.cpp +++ b/func/stream/function_parstate.cpp @@ -15,12 +15,12 @@ int main(int argc, char* argv[]) // get the inputMap (inputdata) std::vector vec = faasm::getInputVec(); - // printf the vector - printf("input data: "); - for (const auto& i : vec) { - printf("%d ", i); - } - printf("\n read data finished. \n"); + // // printf the vector + // printf("input data: "); + // for (const auto& i : vec) { + // printf("%d ", i); + // } + // printf("\n read data finished. \n"); size_t index = 0; // Reset index if reusing buffer std::map> inputMap = @@ -39,7 +39,6 @@ int main(int argc, char* argv[]) printf("parstate readSize: %ld\n", readSize); if (readSize == 0) { printf("function state is still not initilized, initializing it\n"); - functionState["k1"] = { 1, 2, 3, 4, 5 }; functionState["k2"] = { 2, 3, 4, 5, 6, 7, 8 }; functionState["partitionStateKey"] = {}; } else { @@ -61,6 +60,8 @@ int main(int argc, char* argv[]) Begin the loop */ + // Print the size of inputMap + printf("inputMap size: %ld\n", inputMap.size()); for (size_t i = 0; i < inputMap.size(); i++) { // get the input for this spefic function invoke. std::string inputParStr = inputMap[std::to_string(i)]["partitionInputKey"]; diff --git a/func/stream/function_parstate_source.cpp b/func/stream/function_parstate_source.cpp index a0c84d1..56ed16f 100644 --- a/func/stream/function_parstate_source.cpp +++ b/func/stream/function_parstate_source.cpp @@ -16,11 +16,21 @@ int main(int argc, char* argv[]) std::vector vec = faasm::getInputVec(); size_t index = 0; std::map> inputMap = - faasm::deserializeNestedMap(vec,index); + faasm::deserializeNestedMap(vec, index); // printf the inputMap size - printf("inputMap size: %ld\n", inputMap.size()); + printf("inputMap size (Batch Size): %ld\n", inputMap.size()); + // Create a list of words - std::vector messages = { "cat", "dog", "pig" }; + std::vector messages = { + "cat", "dog", "pig", "horse", "cow", + "chicken", "sheep", "goat", "rabbit", "duck", + "turkey", "rooster", "llama", "alpaca", "guinea pig", + "hamster", "ferret", "parrot", "canary", "cockatiel", + "macaw", "parakeet", "budgerigar", "lovebird", "african grey", + "amazon", "cockatoo", "conure", "eclectus", "lorikeet", + "pionus", "quaker", "ringneck", "rosella", "senegal", + "caique" + }; for (int i = 0; i < inputMap.size(); i++) { // Get a random number from 0 to size of messages @@ -35,7 +45,7 @@ int main(int argc, char* argv[]) input["key2"] = "key2value"; std::vector inputBytes; faasm::serializeMap(inputBytes, input); - + faasmChainNamedId( "function_parstate", inputBytes.data(), inputBytes.size(), i); } diff --git a/func/stream/wordcount_count.cpp b/func/stream/wordcount_count.cpp index bc11f42..8253aa2 100644 --- a/func/stream/wordcount_count.cpp +++ b/func/stream/wordcount_count.cpp @@ -54,10 +54,10 @@ int main(int argc, char* argv[]) } // Print the parFunctionState - std::cout << "Printing the partitioned function state" << std::endl; - for (auto const& x : parFunctionState) { - std::cout << x.first << " : " << faasm::uint8VToUint32(x.second) << std::endl; - } + // std::cout << "Printing the partitioned function state" << std::endl; + // for (auto const& x : parFunctionState) { + // std::cout << x.first << " : " << faasm::uint8VToUint32(x.second) << std::endl; + // } /* After the loop diff --git a/func/stream/wordcount_source.cpp b/func/stream/wordcount_source.cpp index b812cc7..99bed35 100644 --- a/func/stream/wordcount_source.cpp +++ b/func/stream/wordcount_source.cpp @@ -24,7 +24,19 @@ int main(int argc, char* argv[]) "an apple a day keeps the doctor away", "four score and seven years ago", "snow white and the seven dwarfs", - "i am at two with nature" + "i am at two with nature", + "the quick brown fox jumps over the lazy dog", + "the early bird catches the worm", + "to be or not to be that is the question", + "ask not what your country can do for you ask what you can do for your country", + "i think therefore i am", + "the only thing we have to fear is fear itself", + "i have nothing to offer but blood toil tears and sweat", + "i came i saw i conquered", + "the pen is mightier than the sword", + "when in the course of human events", + "the bigger they are the harder they fall", + "the best laid schemes of mice" }; // Iterate over the input data and create a random sentence for each diff --git a/func/stream/wordcount_split.cpp b/func/stream/wordcount_split.cpp index d89a91a..b3f8cbe 100644 --- a/func/stream/wordcount_split.cpp +++ b/func/stream/wordcount_split.cpp @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) // call next function. std::string inputSentence = inputMap[std::to_string(i)]["sentence"]; // Print the inputSentence - printf("WordCount split: Received sentence: %s\n", inputSentence.c_str()); + // printf("WordCount split: Received sentence: %s\n", inputSentence.c_str()); std::string word; diff --git a/libfaasm/serialization.cpp b/libfaasm/serialization.cpp index fbb2255..07a78fa 100644 --- a/libfaasm/serialization.cpp +++ b/libfaasm/serialization.cpp @@ -1,8 +1,8 @@ #include "faasm/serialization.h" +#include // For memcpy #include -#include // For memcpy -#include // For std::setw and std::setfill +#include // For std::setw and std::setfill namespace faasm { @@ -12,15 +12,60 @@ inline void hashCombine(std::size_t& seed, std::size_t value) seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); } +std::size_t murmurhash(const uint8_t* key, std::size_t len, uint32_t seed) +{ + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + const uint32_t r1 = 15; + const uint32_t r2 = 13; + const uint32_t m = 5; + const uint32_t n = 0xe6546b64; + + uint32_t hash = seed; + + const int nblocks = len / 4; + const uint32_t* blocks = (const uint32_t*)(key); + for (int i = 0; i < nblocks; i++) { + uint32_t k = blocks[i]; + k *= c1; + k = (k << r1) | (k >> (32 - r1)); + k *= c2; + + hash ^= k; + hash = (hash << r2) | (hash >> (32 - r2)); + hash = hash * m + n; + } + + const uint8_t* tail = (const uint8_t*)(key + nblocks * 4); + uint32_t k1 = 0; + + switch (len & 3) { + case 3: + k1 ^= tail[2] << 16; + case 2: + k1 ^= tail[1] << 8; + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = (k1 << r1) | (k1 >> (32 - r1)); + k1 *= c2; + hash ^= k1; + }; + + hash ^= len; + hash ^= (hash >> 16); + hash *= 0x85ebca6b; + hash ^= (hash >> 13); + hash *= 0xc2b2ae35; + hash ^= (hash >> 16); + + return hash; +} + // Function definition std::size_t hashVector(const std::vector& vec) { - std::size_t hashValue = 0; - for (uint8_t byte : vec) { - std::size_t elementHash = std::hash{}(byte); - hashCombine(hashValue, elementHash); - } - return hashValue; + return murmurhash(vec.data(), vec.size(), 0); } // Transforms a uint8_t vector of bytes into a uint32_t @@ -225,7 +270,8 @@ void appendUint32(std::vector& buffer, uint32_t value) // Serialize a string into a vector of uint8_t void serializeString(std::vector& buffer, const std::string& str) { - appendUint32(buffer, static_cast(str.size())); // Length of string + appendUint32(buffer, + static_cast(str.size())); // Length of string buffer.insert(buffer.end(), str.begin(), str.end()); // String characters } @@ -243,7 +289,8 @@ std::string deserializeString(const std::vector& buffer, size_t& index) void serializeMap(std::vector& buffer, const std::map& map) { - appendUint32(buffer, static_cast(map.size())); // Number of pairs + appendUint32(buffer, + static_cast(map.size())); // Number of pairs for (const auto& pair : map) { serializeString(buffer, pair.first); // Serialize key serializeString(buffer, pair.second); // Serialize value @@ -257,7 +304,7 @@ std::map deserializeMap( { // Handle empty buffer or index out of range if (buffer.size() < index + 4) { - return {}; // Return an empty map if there's not enough data + return {}; // Return an empty map if there's not enough data } uint32_t numPairs = *reinterpret_cast(&buffer[index]); @@ -266,7 +313,8 @@ std::map deserializeMap( for (uint32_t i = 0; i < numPairs; ++i) { if (buffer.size() < index + 1) { - throw std::runtime_error("Buffer too small for expected number of pairs"); + throw std::runtime_error( + "Buffer too small for expected number of pairs"); } std::string key = deserializeString(buffer, index); std::string value = deserializeString(buffer, index); @@ -280,7 +328,9 @@ void serializeNestedMap( std::vector& buffer, const std::map>& nestedMap) { - appendUint32(buffer, static_cast(nestedMap.size())); // Number of outer pairs + appendUint32( + buffer, + static_cast(nestedMap.size())); // Number of outer pairs for (const auto& pair : nestedMap) { serializeString(buffer, pair.first); // Serialize outer key serializeMap(buffer, pair.second); // Serialize inner map @@ -293,7 +343,7 @@ std::map> deserializeNestedMap( size_t& index) { if (buffer.size() < index + 4) { - return {}; // Return an empty nested map if there's not enough data + return {}; // Return an empty nested map if there's not enough data } uint32_t numPairs = *reinterpret_cast(&buffer[index]); @@ -302,13 +352,14 @@ std::map> deserializeNestedMap( for (uint32_t i = 0; i < numPairs; ++i) { if (buffer.size() < index + 1) { - throw std::runtime_error("Buffer too small for expected number of outer pairs"); + throw std::runtime_error( + "Buffer too small for expected number of outer pairs"); } std::string key = deserializeString(buffer, index); - std::map valueMap = deserializeMap(buffer, index); + std::map valueMap = + deserializeMap(buffer, index); nestedMap[std::move(key)] = std::move(valueMap); } return nestedMap; } - } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index cd46eb4..031f752 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ black==22.3.0 # faasmctl==0.13.0 -myfaasmctl==0.0.4 +myfaasmctl==0.0.6 flake8==4.0.1 invoke>=2.0.0 requests>=2.31.0 From 7ee57440ab92034f3a0eb43418a37ef788429df7 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Wed, 5 Jun 2024 22:55:25 +0000 Subject: [PATCH 13/23] feat(stream): Update wordcount example --- func/stream/wordcount_count.cpp | 2 +- func/stream/wordcount_source.cpp | 47 ++++++++++++++++++++------------ requirements.txt | 2 +- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/func/stream/wordcount_count.cpp b/func/stream/wordcount_count.cpp index 8253aa2..e27d1d2 100644 --- a/func/stream/wordcount_count.cpp +++ b/func/stream/wordcount_count.cpp @@ -3,10 +3,10 @@ #include #include #include -#include #include #include +// TODO - auto t0 = std::chrono::system_clock::now(); might contains bug // We must register the function_state in scheduler! int main(int argc, char* argv[]) diff --git a/func/stream/wordcount_source.cpp b/func/stream/wordcount_source.cpp index 99bed35..6cba5c7 100644 --- a/func/stream/wordcount_source.cpp +++ b/func/stream/wordcount_source.cpp @@ -19,24 +19,35 @@ int main(int argc, char* argv[]) faasm::deserializeNestedMap(vec, index); // Create a list of words - std::vector messages = { - "the cow jumped over the moon", - "an apple a day keeps the doctor away", - "four score and seven years ago", - "snow white and the seven dwarfs", - "i am at two with nature", - "the quick brown fox jumps over the lazy dog", - "the early bird catches the worm", - "to be or not to be that is the question", - "ask not what your country can do for you ask what you can do for your country", - "i think therefore i am", - "the only thing we have to fear is fear itself", - "i have nothing to offer but blood toil tears and sweat", - "i came i saw i conquered", - "the pen is mightier than the sword", - "when in the course of human events", - "the bigger they are the harder they fall", - "the best laid schemes of mice" + std::vector sentences = { + "a picture is worth a thousand words but actions speak louder", + "the grass is always greener on the other side of the fence", + "honesty is the best policy especially when dealing with difficult " + "situations", + "time heals all wounds but some scars remain as reminders of the past", + "practice makes perfect so never give up on your dreams and goals", + "you cant judge a book by its cover looks can be deceiving", + "every cloud has a silver lining always look for the positive side", + "an ounce of prevention is worth a pound of cure in health", + "its better to have loved and lost than never to have loved at all", + "a journey of a thousand miles begins with a single step forward", + "absence makes the heart grow fonder but distance can be challenging", + "actions speak louder than words so show your intentions through your " + "deeds", + "birds of a feather flock together finding comfort in similarities", + "when life gives you lemons make lemonade and stay optimistic", + "the early bird catches the worm so wake up early for success", + "knowledge is power so always strive to learn and grow everyday", + "a friend in need is a friend indeed always be supportive", + "better late than never but never late is always better", + "dont count your chickens before they hatch plan your moves wisely", + "good things come to those who wait patience is a virtue", + "two heads are better than one teamwork always leads to success", + "you reap what you sow so always do your very best", + "a penny saved is a penny earned be frugal and wise", + "dont put all your eggs in one basket diversify your efforts", + "fortune favors the brave take risks to achieve great success", + "great minds think alike but fools seldom differ be very discerning" }; // Iterate over the input data and create a random sentence for each diff --git a/requirements.txt b/requirements.txt index 031f752..8e918d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ black==22.3.0 # faasmctl==0.13.0 -myfaasmctl==0.0.6 +myfaasmctl==0.0.7 flake8==4.0.1 invoke>=2.0.0 requests>=2.31.0 From fb0ad90f3d46f65867d73318ee47239bef259fc8 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Wed, 5 Jun 2024 23:03:25 +0000 Subject: [PATCH 14/23] fix(stream): fix wordcount_source bug As title. --- func/stream/wordcount_source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/func/stream/wordcount_source.cpp b/func/stream/wordcount_source.cpp index 6cba5c7..771f6b5 100644 --- a/func/stream/wordcount_source.cpp +++ b/func/stream/wordcount_source.cpp @@ -19,7 +19,7 @@ int main(int argc, char* argv[]) faasm::deserializeNestedMap(vec, index); // Create a list of words - std::vector sentences = { + std::vector messages = { "a picture is worth a thousand words but actions speak louder", "the grass is always greener on the other side of the fence", "honesty is the best policy especially when dealing with difficult " From f46697e45a5ce96a391b7c3a7386ba39e6fbeb89 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Tue, 9 Jul 2024 08:17:42 +0000 Subject: [PATCH 15/23] feat(core): Add Read/Write Partitioned Function State operations As title. --- func/stream/wordcount_count.cpp | 74 +++++++++++++++++++-------------- libfaasm/core.cpp | 18 ++++++++ libfaasm/faasm/core.h | 30 +++++++++++-- libfaasm/faasm/host_interface.h | 18 ++++++-- libfaasm/faasm/input.h | 4 ++ libfaasm/input.cpp | 26 +++++++++++- libfaasm/libfaasm.imports | 5 ++- 7 files changed, 135 insertions(+), 40 deletions(-) diff --git a/func/stream/wordcount_count.cpp b/func/stream/wordcount_count.cpp index e27d1d2..6f3112b 100644 --- a/func/stream/wordcount_count.cpp +++ b/func/stream/wordcount_count.cpp @@ -1,12 +1,15 @@ #include "faasm/input.h" #include +#include #include #include #include #include #include -// TODO - auto t0 = std::chrono::system_clock::now(); might contains bug +// TODO - auto t0 = std::chrono::system_clock::now() might contains bug +// We cannot record time correctly inside the function. Always Overflow. + // We must register the function_state in scheduler! int main(int argc, char* argv[]) @@ -18,22 +21,27 @@ int main(int argc, char* argv[]) std::map> inputMap = faasm::deserializeNestedMap(vec, index); - // get the functionstate - size_t readSize = faasmReadFunctionStateSizeLock(); - std::map> functionState; - if (readSize == 0) { - functionState["partitionStateKey"] = {}; - } else { - std::vector stateBuffer(readSize); - faasmReadFunctionState(stateBuffer.data(), readSize); - functionState = faasm::deserializeFuncState(stateBuffer); + // concat the input string + std::vector inputKeys; + for (size_t i = 0; i < inputMap.size(); i++) { + // get the input for this spefic function invoke. + std::string inputParStr = + inputMap[std::to_string(i)]["partitionInputKey"]; + inputKeys.push_back(inputParStr); } - // get the partitionstate from functionstate - std::map> parFunctionState; - if (functionState["partitionStateKey"].size() != 0) { - parFunctionState = - faasm::deserializeParState(functionState["partitionStateKey"]); + std::string inputKeysStr = faasm::concatInput(inputKeys); + + // get the functionstate + size_t readSize = + faasmReadPartitionedFunctionStateSizeLock(inputKeysStr.c_str()); + + std::map> partitionedState; + if (readSize != 0) { + std::vector stateBuffer(readSize); + faasmReadPartitionedFunctionState( + stateBuffer.data(), readSize, inputKeysStr.c_str()); + partitionedState = faasm::deserializeParState(stateBuffer); } /* @@ -42,33 +50,35 @@ int main(int argc, char* argv[]) for (size_t i = 0; i < inputMap.size(); i++) { // get the input for this spefic function invoke. - std::string inputParStr = inputMap[std::to_string(i)]["partitionInputKey"]; - + std::string inputParStr = + inputMap[std::to_string(i)]["partitionInputKey"]; + // increament the count int count = 0; - if (parFunctionState.find(inputParStr) != parFunctionState.end()) { - count = faasm::uint8VToUint32(parFunctionState[inputParStr]); + if (partitionedState.find(inputParStr) != partitionedState.end()) { + count = faasm::uint8VToUint32(partitionedState[inputParStr]); } count++; - parFunctionState[inputParStr] = faasm::uint32ToUint8V(count); + partitionedState[inputParStr] = faasm::uint32ToUint8V(count); + } + + for (const auto& pair : partitionedState) { + std::cout << pair.first << ": "; + int count = faasm::uint8VToUint32(pair.second); + std::cout << count; + std::cout << std::endl; } - - // Print the parFunctionState - // std::cout << "Printing the partitioned function state" << std::endl; - // for (auto const& x : parFunctionState) { - // std::cout << x.first << " : " << faasm::uint8VToUint32(x.second) << std::endl; - // } /* After the loop */ - functionState["partitionStateKey"] = - faasm::serializeParState(parFunctionState); + // write data back - std::vector functionStateBytes = - faasm::serializeFuncState(functionState); - faasmWriteFunctionStateUnlock(functionStateBytes.data(), - functionStateBytes.size()); + + std::vector partitionedStateBytes = + faasm::serializeParState(partitionedState); + faasmWritePartitionedFunctionStateUnlock(partitionedStateBytes.data(), + partitionedStateBytes.size()); return 0; } diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index 1ca2a82..fd4ead9 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -312,6 +312,24 @@ uint8_t* faasmReadFunctionStatePtrLock() return __faasm_read_function_state_ptr_lock(); } +size_t faasmReadPartitionedFunctionStateSizeLock(const char* inputKeys) +{ + return __faasm_read_partitioned_function_state_size_lock(inputKeys); +} + +long faasmReadPartitionedFunctionState(unsigned char* buffer, + long bufferLen, + const char* inputKeys) +{ + return __faasm_read_partitioned_function_state( + buffer, bufferLen, inputKeys); +} + +void faasmWritePartitionedFunctionStateUnlock(const uint8_t* data, long dataLen) +{ + __faasm_write_partitioned_function_state_unlock(data, dataLen); +} + /** * Write the function state into state server. This function won't create * any functionstate object. It will also unlock the function after writing. diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index 763a1c0..c1d2913 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -168,9 +168,9 @@ extern "C" * Chains a function with the given input data with the current messageIdx */ unsigned int faasmChainNamedId(const char* name, - const uint8_t* inputData, - long inputDataSize, - int idx); + const uint8_t* inputData, + long inputDataSize, + int idx); /** * Chains a function from this module with the given input data @@ -280,6 +280,30 @@ extern "C" */ void faasmWriteFunctionStateUnlock(const uint8_t* data, long dataLen); + /********** + * The following three functions are used for partitioned stateful function. + **********/ + + /** + * Read the size of the partitioned function state, with input keys. We only + * retrieve the needed values. + */ + size_t faasmReadPartitionedFunctionStateSizeLock(const char* inputKeys); + + /** + * Read the the partitioned function state, with input keys. We only + * retrieve the needed values. bufferLen is currently unused. + */ + long faasmReadPartitionedFunctionState(unsigned char* buffer, + long bufferLen, + const char* inputKeys); + + /** + * Write the updated partitioned function state back. + */ + void faasmWritePartitionedFunctionStateUnlock(const uint8_t* data, + long dataLen); + long faasmFunctionStateLock(); void faasmFunctionStateUnlock(); diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index 26cc2bf..3df92ec 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -102,7 +102,7 @@ void __faasm_write_output(const char* output, long outputLen); HOST_IFACE_FUNC unsigned int __faasm_chain_name(const char* name, const unsigned char* inputData, - long inputDataSize, + long inputDataSize, int idx); HOST_IFACE_FUNC @@ -176,8 +176,7 @@ void __faasm_write_function_state(const unsigned char* data, long dataLen); // Read the function state from state server. If it is partitioned stateful, // paasing the inputKeys will return required the states. HOST_IFACE_FUNC -long __faasm_read_function_state(unsigned char* buffer, - long bufferLen); +long __faasm_read_function_state(unsigned char* buffer, long bufferLen); HOST_IFACE_FUNC unsigned char* __faasm_read_function_state_ptr_lock(); @@ -198,4 +197,17 @@ long __faasm_read_function_state_size(int lock); HOST_IFACE_FUNC void __faasm_chain_invoke(); + +HOST_IFACE_FUNC +size_t __faasm_read_partitioned_function_state_size_lock(const char* inputKeys); + +HOST_IFACE_FUNC +long __faasm_read_partitioned_function_state(unsigned char* buffer, + long bufferLen, + const char* inputKeys); + +HOST_IFACE_FUNC +void __faasm_write_partitioned_function_state_unlock(const uint8_t* data, + long dataLen); + #endif \ No newline at end of file diff --git a/libfaasm/faasm/input.h b/libfaasm/faasm/input.h index b379e12..adfa569 100644 --- a/libfaasm/faasm/input.h +++ b/libfaasm/faasm/input.h @@ -14,6 +14,10 @@ void setStringOutput(const char* val); int getIntInput(); int* parseStringToIntArray(const char* inStr, int expected); + +// We use "|" to concat string, please make use the input partitioned keys do +// not contain "|" +const std::string concatInput(const std::vector& input); } #endif diff --git a/libfaasm/input.cpp b/libfaasm/input.cpp index e0b7724..9a96f54 100644 --- a/libfaasm/input.cpp +++ b/libfaasm/input.cpp @@ -25,7 +25,8 @@ const char* getStringInput(const char* defaultValue) } // We use Vec here since, sometimes input cannot cast to string -const std::vector getInputVec(){ +const std::vector getInputVec() +{ long inputSize = faasmGetInputSize(); if (inputSize == 0) { return std::vector(); @@ -66,4 +67,27 @@ int* parseStringToIntArray(const char* strIn, int nInts) return result; } + +const std::string concatInput(const std::vector& input) +{ + std::string result; + bool first = true; // To avoid leading delimiter + + for (const auto& str : input) { + if (str.find('|') != std::string::npos) { + throw std::invalid_argument( + "Input string contains an delimiter character: '|'"); + } + + if (!str.empty()) { + if (!first) { + result += '|'; + } + result += str; + first = false; + } + } + return result; +} + } // namespace faasm diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index f3222be..16e9107 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -54,4 +54,7 @@ __faasm_write_function_state_unlock __faasm_function_state_lock __faasm_function_state_unlock __faasm_read_function_state_size -__faasm_chain_invoke \ No newline at end of file +__faasm_chain_invoke +__faasm_read_partitioned_function_state_size_lock +__faasm_read_partitioned_function_state +__faasm_write_partitioned_function_state_unlock \ No newline at end of file From 05cd773113c2bb1fab98c7f23619bcfdee873ab0 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Mon, 15 Jul 2024 03:36:49 +0000 Subject: [PATCH 16/23] feat(libfaasm): Add individual locks As title. --- func/stream/CMakeLists.txt | 3 + func/stream/wordcount_count.cpp | 12 +-- func/stream/wordcountindiv_count.cpp | 110 ++++++++++++++++++++++++++ func/stream/wordcountindiv_source.cpp | 51 ++++++++++++ func/stream/wordcountindiv_split.cpp | 64 +++++++++++++++ libfaasm/core.cpp | 42 ++++++---- libfaasm/faasm/core.h | 13 +++ libfaasm/faasm/host_interface.h | 13 +++ libfaasm/libfaasm.imports | 5 +- 9 files changed, 289 insertions(+), 24 deletions(-) create mode 100644 func/stream/wordcountindiv_count.cpp create mode 100644 func/stream/wordcountindiv_source.cpp create mode 100644 func/stream/wordcountindiv_split.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index b9d237d..69f6842 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -17,6 +17,9 @@ stream_func(batch_echo batch_echo.cpp) stream_func(wordcount_source wordcount_source.cpp) stream_func(wordcount_split wordcount_split.cpp) stream_func(wordcount_count wordcount_count.cpp) +stream_func(wordcountindiv_source wordcountindiv_source.cpp) +stream_func(wordcountindiv_split wordcountindiv_split.cpp) +stream_func(wordcountindiv_count wordcountindiv_count.cpp) # Custom target to group all the stream functions add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) diff --git a/func/stream/wordcount_count.cpp b/func/stream/wordcount_count.cpp index 6f3112b..6c56f56 100644 --- a/func/stream/wordcount_count.cpp +++ b/func/stream/wordcount_count.cpp @@ -62,12 +62,12 @@ int main(int argc, char* argv[]) partitionedState[inputParStr] = faasm::uint32ToUint8V(count); } - for (const auto& pair : partitionedState) { - std::cout << pair.first << ": "; - int count = faasm::uint8VToUint32(pair.second); - std::cout << count; - std::cout << std::endl; - } + // for (const auto& pair : partitionedState) { + // std::cout << pair.first << ": "; + // int count = faasm::uint8VToUint32(pair.second); + // std::cout << count; + // std::cout << std::endl; + // } /* After the loop diff --git a/func/stream/wordcountindiv_count.cpp b/func/stream/wordcountindiv_count.cpp new file mode 100644 index 0000000..a615dcb --- /dev/null +++ b/func/stream/wordcountindiv_count.cpp @@ -0,0 +1,110 @@ +#include "faasm/input.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO - auto t0 = std::chrono::system_clock::now() might contains bug +// We cannot record time correctly inside the function. Always Overflow. + +// We must register the function_state in scheduler! + +// Function to split a string by a delimiter and store the elements in a set +std::set splitStringToSet(const std::string& str, + const std::string& delimiter) +{ + std::set resultSet; + std::size_t start = 0; + std::size_t end; + std::size_t delimiter_length = delimiter.length(); + + while ((end = str.find(delimiter, start)) != std::string::npos) { + std::string token = str.substr(start, end - start); + if (!token.empty()) { + resultSet.insert(token); + } + start = end + delimiter_length; + } + + // Add the last token if it's not empty + std::string token = str.substr(start); + if (!token.empty()) { + resultSet.insert(token); + } + + return resultSet; +} + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + + size_t index = 0; // Reset index if reusing buffer + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + + // concat the input string + std::vector inputKeys; + std::map todoKeysMap; + for (size_t i = 0; i < inputMap.size(); i++) { + // get the input for this spefic function invoke. + std::string inputParStr = + inputMap[std::to_string(i)]["partitionInputKey"]; + inputKeys.push_back(inputParStr); + if (todoKeysMap.find(inputParStr) != todoKeysMap.end()) { + todoKeysMap[inputParStr]++; + } else { + todoKeysMap[inputParStr] = 1; + } + } + + // BEGIN the loop + while (todoKeysMap.size() > 0) { + std::string inputKeysStr = faasm::concatInput(inputKeys); + // printf("Input keys string: %s\n", inputKeysStr.c_str()); + // printf("the size of inputKeysStr: %zu\n", inputKeysStr.size()); + int lockedKeysSize = inputKeysStr.size() + 1; + // printf("the size of lockedKeysSize: %d\n", lockedKeysSize); + auto lockedKeys = new uint8_t[lockedKeysSize]; + // get the functionstate + size_t readSize = + faasmReadIndivFunctionStateSizeLock(inputKeysStr.c_str(), lockedKeys); + + std::string lockedKeysStr(reinterpret_cast(lockedKeys)); + // printf("Locked keys string: %s\n", lockedKeysStr.c_str()); + auto lockedKeysSet = splitStringToSet(lockedKeysStr, "|"); + + std::map> partitionedState; + if (readSize != 0) { + std::vector stateBuffer(readSize); + faasmReadIndivFunctionState( + stateBuffer.data(), readSize, lockedKeysStr.c_str()); + partitionedState = faasm::deserializeParState(stateBuffer); + } + + for (const std::string& key : lockedKeysSet) { + int count = 0; + if (partitionedState.find(key) != partitionedState.end()) { + count = faasm::uint8VToUint32(partitionedState[key]); + } + count = count + todoKeysMap[key]; + todoKeysMap.erase(key); + partitionedState[key] = faasm::uint32ToUint8V(count); + } + // write data back + + std::vector partitionedStateBytes = + faasm::serializeParState(partitionedState); + faasmWriteIndivFunctionStateUnlock(partitionedStateBytes.data(), + partitionedStateBytes.size()); + } + + // printf("finished"); + + return 0; +} diff --git a/func/stream/wordcountindiv_source.cpp b/func/stream/wordcountindiv_source.cpp new file mode 100644 index 0000000..cee40f9 --- /dev/null +++ b/func/stream/wordcountindiv_source.cpp @@ -0,0 +1,51 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include "faasm/random.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + + // Create a list of words + std::vector messages = { + "a picture is worth a thousand words but actions speak louder", + "the grass is always greener on the other side of the fence", + "practice makes perfect so never give up on your dreams and goals", + "you cant judge a book by its cover looks can be deceiving", + "great minds think alike but fools seldom differ be very discerning" + }; + + // Iterate over the input data and create a random sentence for each + for (int i = 0; i < inputMap.size(); i++) { + // Get a random number from 0 to size of messages + int random_number = faasm::randomInteger(0, messages.size() - 1); + // Return a random sentence + std::string message = messages[random_number]; + + // Prepare for chain call + std::map chainedInput; + chainedInput["sentence"] = message; + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + // Chain call next function. + faasmChainNamedId("wordcountindiv_split", + chainedInputBytes.data(), + chainedInputBytes.size(), + i); + } + + faasmChainInvoke(); + return 0; +} \ No newline at end of file diff --git a/func/stream/wordcountindiv_split.cpp b/func/stream/wordcountindiv_split.cpp new file mode 100644 index 0000000..2912de8 --- /dev/null +++ b/func/stream/wordcountindiv_split.cpp @@ -0,0 +1,64 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + std::vector vec = faasm::getInputVec(); + size_t index = 0; + std::map> inputMap = + faasm::deserializeNestedMap(vec, index); + + // Iterate over the input data and split each sentence + for (int i = 0; i < inputMap.size(); i++) { + // Split the inputSentence by space and store in a vector. Then chained + // call next function. + std::string inputSentence = inputMap[std::to_string(i)]["sentence"]; + // Print the inputSentence + // printf("WordCount split: Received sentence: %s\n", inputSentence.c_str()); + + std::string word; + + for (char x : inputSentence) { + if (x == ' ') { + if (!word.empty()) { + // Prepare for chain call + std::map chainedInput; + chainedInput["partitionInputKey"] = word; + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + faasmChainNamedId("wordcountindiv_count", + chainedInputBytes.data(), + chainedInputBytes.size(), + i); + word.clear(); + } + } else { + word = word + x; + } + } + if (!word.empty()) { + // Prepare for chain call + std::map chainedInput; + chainedInput["partitionInputKey"] = word; + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + + faasmChainNamedId("wordcountindiv_count", + chainedInputBytes.data(), + chainedInputBytes.size(), + i); + } + } + + faasmChainInvoke(); + return 0; +} \ No newline at end of file diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index fd4ead9..c115ac9 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -252,13 +252,13 @@ void faasmCreateFunctionState(const unsigned char* data, long dataLen) * partitioned by the input key, e.g. ID. The stateKey is used to store * the partitioned state. */ -void faasmCreatePartitionedFunctionState(const uint8_t* data, - long dataLen, - const char* inputKey, - const char* stateKey) -{ - __faasm_create_function_state(data, dataLen, inputKey, stateKey); -} +// void faasmCreatePartitionedFunctionState(const uint8_t* data, +// long dataLen, +// const char* inputKey, +// const char* stateKey) +// { +// __faasm_create_function_state(data, dataLen, inputKey, stateKey); +// } /** * Write the Function level state into State Storage @@ -292,16 +292,6 @@ long faasmReadFunctionState(unsigned char* buffer, long bufferLen) return __faasm_read_function_state(buffer, bufferLen); } -// /** -// * Read function state. InputKeys is used for partitioned stateful. -// */ -// long faasmReadParitionedFunctionState(unsigned char* buffer, -// long bufferLen, -// const char* inputKeys) -// { -// return __faasm_read_function_state(buffer, bufferLen, inputKeys); -// } - /** * Read function state data. It returns a pointer of vector. If * nullptr is returned, means this state is created but not initialized. In @@ -330,6 +320,24 @@ void faasmWritePartitionedFunctionStateUnlock(const uint8_t* data, long dataLen) __faasm_write_partitioned_function_state_unlock(data, dataLen); } +unsigned int faasmReadIndivFunctionStateSizeLock(const char* inputKeys, + uint8_t* lockedKeys) +{ + return __faasm_read_indiv_function_state_size_lock(inputKeys, lockedKeys); +} + +long faasmReadIndivFunctionState(unsigned char* buffer, + long bufferLen, + const char* inputKeys) +{ + return __faasm_read_indiv_function_state(buffer, bufferLen, inputKeys); +} + +void faasmWriteIndivFunctionStateUnlock(const uint8_t* data, long dataLen) +{ + __faasm_write_indiv_function_state_unlock(data, dataLen); +} + /** * Write the function state into state server. This function won't create * any functionstate object. It will also unlock the function after writing. diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index c1d2913..8f15e70 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -304,6 +304,19 @@ extern "C" void faasmWritePartitionedFunctionStateUnlock(const uint8_t* data, long dataLen); + /** + * The fllowing access data and hold the 'indivadual' lock. + */ + // Return is the locked key and the size of the state. + unsigned int faasmReadIndivFunctionStateSizeLock(const char* inputKeys, + uint8_t* lockedKeys); + + long faasmReadIndivFunctionState(unsigned char* buffer, + long bufferLen, + const char* inputKeys); + + void faasmWriteIndivFunctionStateUnlock(const uint8_t* data, long dataLen); + long faasmFunctionStateLock(); void faasmFunctionStateUnlock(); diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index 3df92ec..f59bb89 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -210,4 +210,17 @@ HOST_IFACE_FUNC void __faasm_write_partitioned_function_state_unlock(const uint8_t* data, long dataLen); +HOST_IFACE_FUNC +unsigned int __faasm_read_indiv_function_state_size_lock( + const char* inputKeys, + unsigned char* lockedKeys); + +HOST_IFACE_FUNC +long __faasm_read_indiv_function_state(unsigned char* buffer, + long bufferLen, + const char* inputKeys); +HOST_IFACE_FUNC +void __faasm_write_indiv_function_state_unlock(const uint8_t* data, + long dataLen); + #endif \ No newline at end of file diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index 16e9107..9448fab 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -57,4 +57,7 @@ __faasm_read_function_state_size __faasm_chain_invoke __faasm_read_partitioned_function_state_size_lock __faasm_read_partitioned_function_state -__faasm_write_partitioned_function_state_unlock \ No newline at end of file +__faasm_write_partitioned_function_state_unlock +__faasm_read_indiv_function_state_size_lock +__faasm_read_indiv_function_state +__faasm_write_indiv_function_state_unlock \ No newline at end of file From aa66c06fd24a1043418d16834c08e6c2e51059e2 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Wed, 21 Aug 2024 01:42:46 +0000 Subject: [PATCH 17/23] feat(stream): Add stream applications wc, mo and sd As title. --- func/stream/CMakeLists.txt | 5 + func/stream/function_parstate.cpp | 2 +- func/stream/function_parstate_source.cpp | 2 +- func/stream/mo_alert.cpp | 318 +++++++++++++++++++++++ func/stream/mo_anomaly.cpp | 149 +++++++++++ func/stream/mo_score.cpp | 277 ++++++++++++++++++++ func/stream/sd_moving_avg.cpp | 163 ++++++++++++ func/stream/sd_spike_detect.cpp | 40 +++ func/stream/wordcount_count.cpp | 4 +- func/stream/wordcount_split.cpp | 4 +- func/stream/wordcountindiv_count.cpp | 27 +- func/stream/wordcountindiv_split.cpp | 4 +- libfaasm/core.cpp | 8 +- libfaasm/faasm/core.h | 6 + libfaasm/faasm/host_interface.h | 3 + libfaasm/faasm/input.h | 8 + libfaasm/faasm/state.h | 9 + libfaasm/input.cpp | 39 +++ libfaasm/libfaasm.imports | 3 +- libfaasm/state.cpp | 38 +++ 20 files changed, 1087 insertions(+), 22 deletions(-) create mode 100644 func/stream/mo_alert.cpp create mode 100644 func/stream/mo_anomaly.cpp create mode 100644 func/stream/mo_score.cpp create mode 100644 func/stream/sd_moving_avg.cpp create mode 100644 func/stream/sd_spike_detect.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index 69f6842..a0a2ae7 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -20,6 +20,11 @@ stream_func(wordcount_count wordcount_count.cpp) stream_func(wordcountindiv_source wordcountindiv_source.cpp) stream_func(wordcountindiv_split wordcountindiv_split.cpp) stream_func(wordcountindiv_count wordcountindiv_count.cpp) +stream_func(sd_moving_avg sd_moving_avg.cpp) +stream_func(sd_spike_detect sd_spike_detect.cpp) +stream_func(mo_alert mo_alert.cpp) +stream_func(mo_anomaly mo_anomaly.cpp) +stream_func(mo_score mo_score.cpp) # Custom target to group all the stream functions add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) diff --git a/func/stream/function_parstate.cpp b/func/stream/function_parstate.cpp index 7a9215f..556561a 100644 --- a/func/stream/function_parstate.cpp +++ b/func/stream/function_parstate.cpp @@ -64,7 +64,7 @@ int main(int argc, char* argv[]) printf("inputMap size: %ld\n", inputMap.size()); for (size_t i = 0; i < inputMap.size(); i++) { // get the input for this spefic function invoke. - std::string inputParStr = inputMap[std::to_string(i)]["partitionInputKey"]; + std::string inputParStr = inputMap[std::to_string(i)]["partitionedAttribute"]; // print the input par printf("the ith: %zu is inputParStr: %s\n", i, inputParStr.c_str()); diff --git a/func/stream/function_parstate_source.cpp b/func/stream/function_parstate_source.cpp index 56ed16f..099555e 100644 --- a/func/stream/function_parstate_source.cpp +++ b/func/stream/function_parstate_source.cpp @@ -41,7 +41,7 @@ int main(int argc, char* argv[]) message.c_str()); std::map input; - input["partitionInputKey"] = message; + input["partitionedAttribute"] = message; input["key2"] = "key2value"; std::vector inputBytes; faasm::serializeMap(inputBytes, input); diff --git a/func/stream/mo_alert.cpp b/func/stream/mo_alert.cpp new file mode 100644 index 0000000..728a55d --- /dev/null +++ b/func/stream/mo_alert.cpp @@ -0,0 +1,318 @@ +#include "faasm/input.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! + +template +void serializePrimitive(std::vector& buffer, const T& value) +{ + const uint8_t* ptr = reinterpret_cast(&value); + buffer.insert(buffer.end(), ptr, ptr + sizeof(T)); +} + +void serializeVector(std::vector& buffer, + const std::vector& vec) +{ + serializePrimitive(buffer, vec.size()); + buffer.insert(buffer.end(), vec.begin(), vec.end()); +} + +void serializeTuple(std::vector& buffer, + const std::tuple& tuple) +{ + serializePrimitive(buffer, std::get<0>(tuple)); + serializePrimitive(buffer, std::get<1>(tuple)); + serializePrimitive(buffer, std::get<2>(tuple)); + serializePrimitive(buffer, std::get<3>(tuple)); +} + +std::vector serialize( + const std::tuple>>& data) +{ + + std::vector buffer; + + // Serialize the first long + serializePrimitive(buffer, std::get<0>(data)); + + // Serialize the first double + serializePrimitive(buffer, std::get<1>(data)); + + // Serialize the second double + serializePrimitive(buffer, std::get<2>(data)); + + // Serialize the vector of tuples + const auto& vec = std::get<3>(data); + serializePrimitive(buffer, vec.size()); // Serialize the size of the vector + + for (const auto& tuple : vec) { + serializeTuple(buffer, tuple); + } + + return buffer; +} + +template +void deserializePrimitive(const std::vector& buffer, + size_t& offset, + T& value) +{ + std::memcpy(&value, buffer.data() + offset, sizeof(T)); + offset += sizeof(T); +} + +std::tuple deserializeTuple( + const std::vector& buffer, + size_t& offset) +{ + int first; + double second, third; + long fourth; + + deserializePrimitive(buffer, offset, first); + deserializePrimitive(buffer, offset, second); + deserializePrimitive(buffer, offset, third); + deserializePrimitive(buffer, offset, fourth); + + return std::make_tuple(first, second, third, fourth); +} + +std::tuple>> +deserialize(const std::vector& buffer) +{ + + size_t offset = 0; + + // Deserialize the first long + long first; + deserializePrimitive(buffer, offset, first); + + // Deserialize the first double + double second; + deserializePrimitive(buffer, offset, second); + + // Deserialize the second double + double third; + deserializePrimitive(buffer, offset, third); + + // Deserialize the vector of tuples + size_t vecSize; + deserializePrimitive(buffer, offset, vecSize); + + std::vector> vec; + vec.reserve(vecSize); + for (size_t i = 0; i < vecSize; ++i) { + vec.push_back(deserializeTuple(buffer, offset)); + } + + return std::make_tuple(first, second, third, vec); +} + +int partition(std::vector>& arr, + int left, + int right) +{ + int pivotIdx = right; + auto pivot = arr[pivotIdx]; + int bar = left - 1; + + for (int i = left; i < right; ++i) { + if (std::get<2>(arr[i]) < std::get<2>(pivot)) { + bar++; + std::swap(arr[bar], arr[i]); + } + } + std::swap(arr[bar + 1], arr[pivotIdx]); + return bar + 1; +} + +// Select the i-th smallest element based on the third element in the tuple +std::tuple select( + std::vector>& arr, + int i, + int left, + int right) +{ + if (left == right) { + return arr[right]; + } + + int p = partition(arr, left, right); + + if (p == i) { + return arr[p]; + } else if (p < i) { + return select(arr, i, p + 1, right); + } else { + return select(arr, i, left, p - 1); + } +} + +void BFPRT(std::vector>& arr, int k) +{ + auto index = select(arr, k, 0, arr.size() - 1); +} + +void identifyAbnormal( + std::vector>& streamList) +{ + int median = streamList.size() / 2; + BFPRT(streamList, median); +} + +// +// std::vector> streamList; + +// +// std::tuple> + +int main(int argc, char* argv[]) +{ + const double dupper = std::sqrt(2); + + // get the inputMap (inputdata) + auto inputMap = faasm::getInputMap(); + + // Get the Function + size_t readSize = faasmReadFunctionStateSizeLock(); + long prevTimestamp; + double minScore; + double maxScore; + // + std::vector> streamList; + if (readSize == 0) { + // Initialize the Function State + prevTimestamp = 0; + minScore = std::numeric_limits::max(); + maxScore = 0; + } else { + std::vector stateBytes(readSize); + faasmReadFunctionState(stateBytes.data(), readSize); + auto functionState = deserialize(stateBytes); + prevTimestamp = std::get<0>(functionState); + minScore = std::get<1>(functionState); + maxScore = std::get<2>(functionState); + streamList = std::get<3>(functionState); + } + // Print the prevTimestamp, minScore, maxScore, streamList.size() + // std::ostringstream oss; + // oss << "prevTimestamp: " << prevTimestamp << " minScore: " << minScore + // << " maxScore: " << maxScore + // << " streamList.size(): " << streamList.size(); + // std::cout << oss.str() << std::endl; + + // Process each request + for (int i = 0; i < inputMap.size(); i++) { + // Get the input for this spefic function invoke. + int machineId = std::stoi(inputMap[std::to_string(i)]["machineId"]); + double score = std::stod(inputMap[std::to_string(i)]["score"]); + double sumScore = std::stod(inputMap[std::to_string(i)]["sumScore"]); + long timestamp = std::stol(inputMap[std::to_string(i)]["timestamp"]); + if (timestamp > prevTimestamp) { + if (streamList.size() != 0) { + // Print the streamList sumScore before reorder + // std::ostringstream oss1; + // oss1 << "Before Reorder: "; + // for (auto streamProfile : streamList) { + // oss1 << std::get<2>(streamProfile) << " "; + // } + // std::cout << oss1.str() << std::endl; + identifyAbnormal(streamList); + // Print the streamList sumScore after reorder + // std::ostringstream oss2; + // oss2 << "After Reorder: "; + // for (auto streamProfile : streamList) { + // oss2 << std::get<2>(streamProfile) << " "; + // } + // std::cout << oss2.str() << std::endl; + std::string output; + int median = streamList.size() / 2; + int minScoreTemp = std::get<2>(streamList[0]); + int medianScoreTemp = std::get<2>(streamList[median]); + for (auto streamProfile : streamList) { + double streamScore = std::get<2>(streamProfile); + double dataScore = std::get<1>(streamProfile); + bool isAbnormal = false; + if ((streamScore > 2 * medianScoreTemp - minScoreTemp) && + (streamScore > minScoreTemp + 2 * dupper)) { + if (dataScore > 0.1 + minScore) { + isAbnormal = true; + } + } + + if (isAbnormal) { + // Print the streamScore and dataScore of the abnormal + // data + // std::ostringstream oss3; + // oss3 << "Stream Score: " << streamScore + // << " Data Score: " << dataScore; + // std::cout << oss3.str() << std::endl; + output += + std::to_string(std::get<0>(streamProfile)) + " " + + std::to_string(std::get<3>(streamProfile)) + " "; + } + } + if (output.size() > 0) { + faasmSetOutputId(output.c_str(), output.size(), i); + } + + streamList.clear(); + double minScore = std::numeric_limits::max(); + maxScore = 0; + } + prevTimestamp = timestamp; + } + + if (score > maxScore) { + maxScore = score; + } + if (score < minScore) { + minScore = score; + } + streamList.push_back( + std::make_tuple(machineId, score, sumScore, timestamp)); + } + auto dataTuple = + std::make_tuple(prevTimestamp, minScore, maxScore, streamList); + // Print the dataTuple + // std::ostringstream oss4; + // oss4 << "Data Tuple: "; + // oss4 << "prevTimestamp: " << prevTimestamp << " minScore: " << minScore + // << " maxScore: " << maxScore + // << " streamList.size(): " << streamList.size() << std::endl; + // Print the streamList + // for (auto streamProfile : streamList) { + // oss4 << " machineId: " << std::get<0>(streamProfile) + // << " score: " << std::get<1>(streamProfile) + // << " sumScore: " << std::get<2>(streamProfile) + // << " timestamp: " << std::get<3>(streamProfile) << std::endl; + // } + // std::cout << oss4.str() << std::endl; + std::vector stateBytes = serialize(dataTuple); + faasmWriteFunctionStateUnlock(stateBytes.data(), stateBytes.size()); + return 0; +} diff --git a/func/stream/mo_anomaly.cpp b/func/stream/mo_anomaly.cpp new file mode 100644 index 0000000..56c1175 --- /dev/null +++ b/func/stream/mo_anomaly.cpp @@ -0,0 +1,149 @@ +#include "faasm/input.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! + +// Function to serialize a std::list to std::vector +std::vector serialize(const std::list& data) +{ + std::vector buffer; + for (const double& value : data) { + uint8_t bytes[sizeof(double)]; + std::memcpy(bytes, &value, sizeof(double)); + buffer.insert(buffer.end(), bytes, bytes + sizeof(double)); + } + return buffer; +} + +// Function to deserialize a std::vector back to std::list +std::list deserialize(const std::vector& buffer) +{ + std::list data; + if (buffer.size() % sizeof(double) != 0) { + throw std::runtime_error("Invalid buffer size for deserialization."); + } + for (size_t i = 0; i < buffer.size(); i += sizeof(double)) { + double value; + std::memcpy(&value, buffer.data() + i, sizeof(double)); + data.push_back(value); + } + return data; +} + +int main(int argc, char* argv[]) +{ + int windowlength = 100; + // get the inputMap (inputdata) + auto inputMap = faasm::getInputMap(); + + // concat the input string. + // > + std::map>> + todoKeysMap; + + for (size_t i = 0; i < inputMap.size(); i++) { + // get the input for this spefic function invoke. + std::string inputAttr = + inputMap[std::to_string(i)]["partitionedAttribute"]; + double score = std::stod(inputMap[std::to_string(i)]["score"]); + long timestamp = std::stol(inputMap[std::to_string(i)]["timestamp"]); + todoKeysMap[inputAttr].push_back( + std::tuple(i, score, timestamp)); + } + + // Print the input keys (partitionedAttribute) + // std::ostringstream oss; + // oss << "Partitioned Attributes: "; + // for (const auto& pair : todoKeysMap) { + // oss << pair.first << " "; + // } + // std::cout << oss.str() << std::endl; + + while (todoKeysMap.size() > 0) { + // Collect todoKeys to string + std::vector todoKeys; + for (const auto& pair : todoKeysMap) { + todoKeys.push_back(pair.first); + } + auto lockedStates = faasm::getPartitionedStates(todoKeys); + const auto& lockedKeysSet = lockedStates.first; + auto& partitionedState = lockedStates.second; + // For each locked key, process the requests. + for (const std::string& key : lockedKeysSet) { + // Prepare states of partitioned attribute 'key' + std::list pastScores; + if (partitionedState.find(key) != partitionedState.end()) { + pastScores = deserialize(partitionedState.at(key)); + } + // Process requests of partitioned attribute 'key' + for (const auto& todoData : todoKeysMap[key]) { + size_t idx = std::get<0>(todoData); + double score = std::get<1>(todoData); + long timestamp = std::get<2>(todoData); + + pastScores.push_back(score); + if (pastScores.size() == windowlength) { + pastScores.pop_front(); + } + + double sumScore = 0.0; + for (const double& pastScore : pastScores) { + sumScore += pastScore; + } + + // Print the partitioned attribute, sumScore and timestamp + // std::ostringstream oss1; + // oss1 << "Partitioned Attribute: " << key + // << ", Sum Score: " << sumScore + // << ", Timestamp: " << timestamp; + // std::cout << oss1.str() << std::endl; + + // Chained call next function + std::map chainedInput; + chainedInput["machineId"] = key; + chainedInput["score"] = std::to_string(score); + chainedInput["timestamp"] = std::to_string(timestamp); + chainedInput["sumScore"] = std::to_string(sumScore); + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + faasmChainNamedId("mo_alert", + chainedInputBytes.data(), + chainedInputBytes.size(), + idx); + } + std::vector newPastScoresBytes = serialize(pastScores); + partitionedState[key] = newPastScoresBytes; + todoKeysMap.erase(key); + } + // For each locked key, print the partitioned attribute and past scores + // for (const auto& pair : partitionedState) { + // std::ostringstream oss2; + // oss2 << "Partitioned Attribute: " << pair.first + // << ", Past Scores: "; + // for (const double& pastScore : deserialize(pair.second)) { + // oss2 << pastScore << " "; + // } + // std::cout << oss2.str() << std::endl; + // } + std::vector partitionedStateBytes = + faasm::serializeParState(partitionedState); + faasmWriteIndivFunctionStateUnlock(partitionedStateBytes.data(), + partitionedStateBytes.size()); + } + + faasmChainInvoke(); + return 0; +} diff --git a/func/stream/mo_score.cpp b/func/stream/mo_score.cpp new file mode 100644 index 0000000..1f3560c --- /dev/null +++ b/func/stream/mo_score.cpp @@ -0,0 +1,277 @@ +#include "faasm/input.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! + +std::vector serialize( + const std::vector>& observationList, + long prevTimestamp) +{ + std::vector buffer; + + // Serialize the size of the vector + size_t vectorSize = observationList.size(); + buffer.resize(buffer.size() + sizeof(vectorSize)); + std::memcpy(buffer.data() + buffer.size() - sizeof(vectorSize), + &vectorSize, + sizeof(vectorSize)); + + // Serialize each tuple in the vector + for (const auto& tuple : observationList) { + int machineId; + double cpu, mem; + long timestamp; + + std::tie(machineId, cpu, mem, timestamp) = tuple; + + // Serialize each element of the tuple + buffer.resize(buffer.size() + sizeof(machineId)); + std::memcpy(buffer.data() + buffer.size() - sizeof(machineId), + &machineId, + sizeof(machineId)); + + buffer.resize(buffer.size() + sizeof(cpu)); + std::memcpy( + buffer.data() + buffer.size() - sizeof(cpu), &cpu, sizeof(cpu)); + + buffer.resize(buffer.size() + sizeof(mem)); + std::memcpy( + buffer.data() + buffer.size() - sizeof(mem), &mem, sizeof(mem)); + + buffer.resize(buffer.size() + sizeof(timestamp)); + std::memcpy(buffer.data() + buffer.size() - sizeof(timestamp), + ×tamp, + sizeof(timestamp)); + } + + // Serialize the long value + buffer.resize(buffer.size() + sizeof(prevTimestamp)); + std::memcpy(buffer.data() + buffer.size() - sizeof(prevTimestamp), + &prevTimestamp, + sizeof(prevTimestamp)); + + return buffer; +} + +void deserialize( + const std::vector& buffer, + std::vector>& observationList, + long& prevTimestamp) +{ + size_t offset = 0; + + // Deserialize the size of the vector + size_t vectorSize; + std::memcpy(&vectorSize, buffer.data() + offset, sizeof(vectorSize)); + offset += sizeof(vectorSize); + + // Resize the vector to hold the deserialized data + observationList.resize(vectorSize); + + // Deserialize each tuple in the vector + for (size_t i = 0; i < vectorSize; ++i) { + int machineId; + double cpu, mem; + long timestamp; + + std::memcpy(&machineId, buffer.data() + offset, sizeof(machineId)); + offset += sizeof(machineId); + + std::memcpy(&cpu, buffer.data() + offset, sizeof(cpu)); + offset += sizeof(cpu); + + std::memcpy(&mem, buffer.data() + offset, sizeof(mem)); + offset += sizeof(mem); + + std::memcpy(×tamp, buffer.data() + offset, sizeof(timestamp)); + offset += sizeof(timestamp); + + observationList[i] = std::make_tuple(machineId, cpu, mem, timestamp); + } + + // Deserialize the long value + std::memcpy(&prevTimestamp, buffer.data() + offset, sizeof(prevTimestamp)); +} + +std::vector calculateDistance(std::vector>& matrix) +{ + int numCol = matrix[0].size(); // It is 2 in this case + + // CPU and Mem normalization + std::vector mins = { 0.0, 0.0 }; + std::vector maxs = { 100.0, 100.0 }; + std::vector centers(numCol, 0.0); + + for (int col = 0; col < numCol; ++col) { + for (int row = 0; row < matrix.size(); ++row) { + matrix[row][col] = + (matrix[row][col] - mins[col]) / (maxs[col] - mins[col]); + centers[col] += matrix[row][col]; + } + centers[col] /= matrix.size(); + } + + std::vector> distances( + matrix.size(), std::vector(numCol, 0.0)); + + // Calculate the absolute distance from the center + for (int row = 0; row < matrix.size(); ++row) { + for (int col = 0; col < numCol; ++col) { + distances[row][col] = std::abs(matrix[row][col] - centers[col]); + } + } + + std::vector l2distances(matrix.size(), 0.0); + + // Calculate the L2 distance (Euclidean distance) + for (int row = 0; row < l2distances.size(); ++row) { + for (int col = 0; col < numCol; ++col) { + l2distances[row] += std::pow(distances[row][col], 2); + } + l2distances[row] = std::sqrt(l2distances[row]); + } + + return l2distances; +} + +/*** + * Observation std::tuple = (machineId, cpu, mem, + * timestamp) + * Score std::tuple = (machineId, score, + * timestamp) + * Matrix is like: + * x / y 1 2 + * 1 c1 m1 + * 2 c2 m2 + * 3 c3 m3 + * ***/ + +std::vector getScores( + std::vector>& observationList) +{ + // Initialize the matrix + std::vector> matrix(observationList.size(), + std::vector(2)); + for (size_t i = 0; i < observationList.size(); ++i) { + matrix[i][0] = std::get<1>(observationList[i]); // cpu + matrix[i][1] = std::get<2>(observationList[i]); // memory + } + + std::vector l2distances = calculateDistance(matrix); + + for (double& distance : l2distances) { + distance += 1.0; + } + + return l2distances; +} + +// Observation std::tuple = (machineId, cpu, mem, +// timestamp) +// Score std::tuple = (machineId, score, timestamp) + +int main(int argc, char* argv[]) +{ + // get the inputMap (inputdata) + auto inputMap = faasm::getInputMap(); + + // Get and initialize the Function + size_t readSize = faasmReadFunctionStateSizeLock(); + std::vector> observationList; + long prevTimestamp; + if (readSize == 0) { + long previousTime = 0; + } else { + std::vector stateBytes(readSize); + faasmReadFunctionState(stateBytes.data(), readSize); + deserialize(stateBytes, observationList, prevTimestamp); + } + + // Process each request + for (int i = 0; i < inputMap.size(); i++) { + // Get the input for this spefic function invoke. + int machineId = std::stoi(inputMap[std::to_string(i)]["machineId"]); + double cpu = std::stod(inputMap[std::to_string(i)]["cpu"]); + double mem = std::stod(inputMap[std::to_string(i)]["mem"]); + long timestamp = std::stol(inputMap[std::to_string(i)]["timestamp"]); + // Print the input data and previous timestamp + // std::ostringstream oss; + // oss << "Input " << i << ": MachineId = " << machineId + // << ", CPU = " << cpu << ", Memory = " << mem + // << ", Timestamp = " << timestamp + // << ", Previous Timestamp = " << prevTimestamp; + // std::cout << oss.str() << std::endl; + if (timestamp > prevTimestamp) { + // After getting a new batch, calculate the scores of old batch + if (observationList.size() > 0) { + // Calculate the score + auto scores = getScores(observationList); + // Print the scores + // std::ostringstream oss1; + // oss1 << "Scores: "; + // for (const auto& score : scores) { + // oss1 << score << " "; + // } + // std::cout << oss1.str() << std::endl; + // Send messages to the next function + for (size_t j = 0; j < observationList.size(); j++) { + int machineId = std::get<0>(observationList[j]); + double score = scores[j]; + long timestamp = std::get<3>(observationList[j]); + + // Prepare for chain call + std::map chainedInput; + chainedInput["partitionedAttribute"] = + std::to_string(machineId); + chainedInput["score"] = std::to_string(score); + chainedInput["timestamp"] = std::to_string(timestamp); + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + + faasmChainNamedId("mo_anomaly", + chainedInputBytes.data(), + chainedInputBytes.size(), + i); + } + + // Clear the observationList + observationList.clear(); + } + prevTimestamp = timestamp; + } + observationList.push_back( + std::make_tuple(machineId, cpu, mem, timestamp)); + } + + // print the machine id in the observation list and previous timestamp + // std::ostringstream oss2; + // oss2 << "MachineId in the observation list: "; + // for (const auto& tuple : observationList) { + // oss2 << std::get<0>(tuple) << " "; + // } + // oss2 << ", Previous Timestamp = " << prevTimestamp; + // std::cout << oss2.str() << std::endl; + + std::vector stateBytes = serialize(observationList, prevTimestamp); + faasmWriteFunctionStateUnlock(stateBytes.data(), stateBytes.size()); + + faasmChainInvoke(); + return 0; +} diff --git a/func/stream/sd_moving_avg.cpp b/func/stream/sd_moving_avg.cpp new file mode 100644 index 0000000..9de70e0 --- /dev/null +++ b/func/stream/sd_moving_avg.cpp @@ -0,0 +1,163 @@ +#include "faasm/input.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! + +std::vector serialize(const std::pair>& p) +{ + std::vector buffer; + + // Serialize the first double + const uint8_t* double_data = reinterpret_cast(&p.first); + buffer.insert(buffer.end(), double_data, double_data + sizeof(double)); + + // Serialize the size of the list + size_t size = p.second.size(); + const uint8_t* size_data = reinterpret_cast(&size); + buffer.insert(buffer.end(), size_data, size_data + sizeof(size)); + + // Serialize each double in the list + for (double val : p.second) { + const uint8_t* val_data = reinterpret_cast(&val); + buffer.insert(buffer.end(), val_data, val_data + sizeof(double)); + } + + return buffer; +} + +std::pair> deserialize( + const std::vector& buffer) +{ + size_t offset = 0; + + // Deserialize the first double + double first; + std::memcpy(&first, buffer.data() + offset, sizeof(double)); + offset += sizeof(double); + + // Deserialize the size of the list + size_t size; + std::memcpy(&size, buffer.data() + offset, sizeof(size)); + offset += sizeof(size); + + // Deserialize each double and populate the list + std::list second; + for (size_t i = 0; i < size; ++i) { + double val; + std::memcpy(&val, buffer.data() + offset, sizeof(double)); + offset += sizeof(double); + second.push_back(val); + } + + return std::move(std::make_pair(first, second)); +} + +int main(int argc, char* argv[]) +{ + int windowlength = 100; + // get the inputMap (inputdata) + auto inputMap = faasm::getInputMap(); + + // concat the input string. + // > + std::map>> todoKeysMap; + for (size_t i = 0; i < inputMap.size(); i++) { + // get the input for this spefic function invoke. + std::string inputAttr = + inputMap[std::to_string(i)]["partitionedAttribute"]; + double inputData = + std::stod(inputMap[std::to_string(i)]["temperature"]); + todoKeysMap[inputAttr].push_back( + std::tuple(i, inputData)); + } + + while (todoKeysMap.size() > 0) { + // Collect todoKeys to string + std::vector todoKeys; + for (const auto& pair : todoKeysMap) { + todoKeys.push_back(pair.first); + } + auto lockedStates = faasm::getPartitionedStates(todoKeys); + const auto& lockedKeysSet = lockedStates.first; + auto& partitionedState = lockedStates.second; + // For each locked key, process the requests. + for (const std::string& key : lockedKeysSet) { + // Prepare states of partitioned attribute 'key' + std::pair> statistics; + if (partitionedState.find(key) != partitionedState.end()) { + statistics = deserialize(partitionedState.at(key)); + } else { + statistics = { 0.0, std::list() }; + } + double& sum = statistics.first; + std::list& values = statistics.second; + + // Process requests of partitioned attribute 'key' + for (const std::tuple& todoData : + todoKeysMap[key]) { + size_t idx = std::get<0>(todoData); + double todoValue = std::get<1>(todoData); + + if (values.size() >= windowlength) { + sum -= values.front(); + values.pop_front(); + } + sum += todoValue; + values.push_back(todoValue); + double avg = sum / values.size(); + // Chained call next function + std::map chainedInput; + chainedInput["movingAverage"] = std::to_string(avg); + chainedInput["temperature"] = std::to_string(todoValue); + std::vector chainedInputBytes; + faasm::serializeMap(chainedInputBytes, chainedInput); + faasmChainNamedId("sd_spike_detect", + chainedInputBytes.data(), + chainedInputBytes.size(), + idx); + } + // Print : used for testing + { + std::string valuesStr = "["; + for (auto it = values.begin(); it != values.end(); ++it) { + valuesStr += std::to_string(*it); + if (std::next(it) != values.end()) { + valuesStr += ", "; + } + } + valuesStr += "]"; + + // Print out statistics including the values in one line + std::cout << "Key: " << key << ", Count: " << values.size() + << ", Sum: " << sum + << ", Average: " << (sum / values.size()) + << ", Values: " << valuesStr << std::endl; + } + std::pair> newStatistics = { sum, + values }; + std::vector newStatisticsBytes = serialize(newStatistics); + partitionedState[key] = newStatisticsBytes; + todoKeysMap.erase(key); + } + std::vector partitionedStateBytes = + faasm::serializeParState(partitionedState); + faasmWriteIndivFunctionStateUnlock(partitionedStateBytes.data(), + partitionedStateBytes.size()); + } + + faasmChainInvoke(); + + return 0; +} diff --git a/func/stream/sd_spike_detect.cpp b/func/stream/sd_spike_detect.cpp new file mode 100644 index 0000000..1d3d111 --- /dev/null +++ b/func/stream/sd_spike_detect.cpp @@ -0,0 +1,40 @@ +#include "faasm/core.h" +#include "faasm/faasm.h" +#include "faasm/input.h" +#include + +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + double spikeThreshold = 0.03; // Replace with actual value + // get the inputMap + auto inputMap = faasm::getInputMap(); + + // Iterate over the input data and split each sentence + for (int i = 0; i < inputMap.size(); i++) { + double movingAverage = + std::stod(inputMap[std::to_string(i)]["movingAverage"]); + double temperature = + std::stod(inputMap[std::to_string(i)]["temperature"]); + if (std::abs(temperature - movingAverage) > + spikeThreshold * movingAverage) { + std::string output = "detected spike"; + faasmSetOutputId(output.c_str(), output.size(), i); + std::cout << "Input " << i << ": Moving Average = " << movingAverage + << ", Temperature = " << temperature + << ", Status = Detected" << std::endl; + } else { + std::string output = "no spike"; + faasmSetOutputId(output.c_str(), output.size(), i); + std::cout << "Input " << i << ": Moving Average = " << movingAverage + << ", Temperature = " << temperature + << ", Status = Undetected" << std::endl; + } + } + + return 0; +} \ No newline at end of file diff --git a/func/stream/wordcount_count.cpp b/func/stream/wordcount_count.cpp index 6c56f56..4b59ad6 100644 --- a/func/stream/wordcount_count.cpp +++ b/func/stream/wordcount_count.cpp @@ -26,7 +26,7 @@ int main(int argc, char* argv[]) for (size_t i = 0; i < inputMap.size(); i++) { // get the input for this spefic function invoke. std::string inputParStr = - inputMap[std::to_string(i)]["partitionInputKey"]; + inputMap[std::to_string(i)]["partitionedAttribute"]; inputKeys.push_back(inputParStr); } @@ -51,7 +51,7 @@ int main(int argc, char* argv[]) for (size_t i = 0; i < inputMap.size(); i++) { // get the input for this spefic function invoke. std::string inputParStr = - inputMap[std::to_string(i)]["partitionInputKey"]; + inputMap[std::to_string(i)]["partitionedAttribute"]; // increament the count int count = 0; diff --git a/func/stream/wordcount_split.cpp b/func/stream/wordcount_split.cpp index b3f8cbe..96fc890 100644 --- a/func/stream/wordcount_split.cpp +++ b/func/stream/wordcount_split.cpp @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) if (!word.empty()) { // Prepare for chain call std::map chainedInput; - chainedInput["partitionInputKey"] = word; + chainedInput["partitionedAttribute"] = word; std::vector chainedInputBytes; faasm::serializeMap(chainedInputBytes, chainedInput); faasmChainNamedId("wordcount_count", @@ -48,7 +48,7 @@ int main(int argc, char* argv[]) if (!word.empty()) { // Prepare for chain call std::map chainedInput; - chainedInput["partitionInputKey"] = word; + chainedInput["partitionedAttribute"] = word; std::vector chainedInputBytes; faasm::serializeMap(chainedInputBytes, chainedInput); diff --git a/func/stream/wordcountindiv_count.cpp b/func/stream/wordcountindiv_count.cpp index a615dcb..1434ddd 100644 --- a/func/stream/wordcountindiv_count.cpp +++ b/func/stream/wordcountindiv_count.cpp @@ -49,13 +49,12 @@ int main(int argc, char* argv[]) faasm::deserializeNestedMap(vec, index); // concat the input string - std::vector inputKeys; + std::vector todoKeys; std::map todoKeysMap; for (size_t i = 0; i < inputMap.size(); i++) { // get the input for this spefic function invoke. std::string inputParStr = - inputMap[std::to_string(i)]["partitionInputKey"]; - inputKeys.push_back(inputParStr); + inputMap[std::to_string(i)]["parititonedAttribute"]; if (todoKeysMap.find(inputParStr) != todoKeysMap.end()) { todoKeysMap[inputParStr]++; } else { @@ -65,18 +64,19 @@ int main(int argc, char* argv[]) // BEGIN the loop while (todoKeysMap.size() > 0) { - std::string inputKeysStr = faasm::concatInput(inputKeys); - // printf("Input keys string: %s\n", inputKeysStr.c_str()); - // printf("the size of inputKeysStr: %zu\n", inputKeysStr.size()); - int lockedKeysSize = inputKeysStr.size() + 1; - // printf("the size of lockedKeysSize: %d\n", lockedKeysSize); + todoKeys.clear(); + for (const auto& pair : todoKeysMap) { + todoKeys.push_back(pair.first); + } + std::string todoKeysStr = faasm::concatInput(todoKeys); + // Initialize the locked keys + int lockedKeysSize = todoKeysStr.size() + 1; auto lockedKeys = new uint8_t[lockedKeysSize]; // get the functionstate size_t readSize = - faasmReadIndivFunctionStateSizeLock(inputKeysStr.c_str(), lockedKeys); + faasmReadIndivFunctionStateSizeLock(todoKeysStr.c_str(), lockedKeys); std::string lockedKeysStr(reinterpret_cast(lockedKeys)); - // printf("Locked keys string: %s\n", lockedKeysStr.c_str()); auto lockedKeysSet = splitStringToSet(lockedKeysStr, "|"); std::map> partitionedState; @@ -97,7 +97,12 @@ int main(int argc, char* argv[]) partitionedState[key] = faasm::uint32ToUint8V(count); } // write data back - + for (const auto& pair : partitionedState) { + std::cout << pair.first << ": "; + int count = faasm::uint8VToUint32(pair.second); + std::cout << count; + std::cout << std::endl; + } std::vector partitionedStateBytes = faasm::serializeParState(partitionedState); faasmWriteIndivFunctionStateUnlock(partitionedStateBytes.data(), diff --git a/func/stream/wordcountindiv_split.cpp b/func/stream/wordcountindiv_split.cpp index 2912de8..4df08a3 100644 --- a/func/stream/wordcountindiv_split.cpp +++ b/func/stream/wordcountindiv_split.cpp @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) if (!word.empty()) { // Prepare for chain call std::map chainedInput; - chainedInput["partitionInputKey"] = word; + chainedInput["partitionedAttribute"] = word; std::vector chainedInputBytes; faasm::serializeMap(chainedInputBytes, chainedInput); faasmChainNamedId("wordcountindiv_count", @@ -48,7 +48,7 @@ int main(int argc, char* argv[]) if (!word.empty()) { // Prepare for chain call std::map chainedInput; - chainedInput["partitionInputKey"] = word; + chainedInput["partitionedAttribute"] = word; std::vector chainedInputBytes; faasm::serializeMap(chainedInputBytes, chainedInput); diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index c115ac9..30dc69a 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -166,7 +166,7 @@ unsigned int faasmChainNamed(const char* name, } // This function is desiged for batch processing, the message has to call the -// with idx +// with current msg idx (used for chained record) unsigned int faasmChainNamedId(const char* name, const uint8_t* inputData, long inputDataSize, @@ -360,4 +360,8 @@ void faasmFunctionStateUnlock() void faasmChainInvoke() { __faasm_chain_invoke(); -} \ No newline at end of file +} + +void faasmSetOutputId(const char* newOutput, long outputLen, int idx){ + __faasm_set_output_id(newOutput, outputLen, idx); +} diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index 8f15e70..b2657a5 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -323,6 +323,12 @@ extern "C" void faasmChainInvoke(); + /** + * Sets the given string as the output data for this function of specified + * Msg in the batch processing + */ + void faasmSetOutputId(const char* newOutput, long outputLen, int idx); + // Macro for defining zygotes (a default fallback noop is provided) int __attribute__((weak)) _faasm_zygote(); #define FAASM_ZYGOTE() int _faasm_zygote() diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index f59bb89..a5e5b00 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -223,4 +223,7 @@ HOST_IFACE_FUNC void __faasm_write_indiv_function_state_unlock(const uint8_t* data, long dataLen); +HOST_IFACE_FUNC +void __faasm_set_output_id(const char* newOutput, long outputLen, int idx); + #endif \ No newline at end of file diff --git a/libfaasm/faasm/input.h b/libfaasm/faasm/input.h index adfa569..9007903 100644 --- a/libfaasm/faasm/input.h +++ b/libfaasm/faasm/input.h @@ -2,6 +2,9 @@ #define FAASM_INPUT_H #include "faasm/core.h" +#include +#include +#include #include namespace faasm { @@ -9,6 +12,8 @@ const char* getStringInput(const char* defaultValue); const std::vector getInputVec(); +const std::map> getInputMap(); + void setStringOutput(const char* val); int getIntInput(); @@ -18,6 +23,9 @@ int* parseStringToIntArray(const char* inStr, int expected); // We use "|" to concat string, please make use the input partitioned keys do // not contain "|" const std::string concatInput(const std::vector& input); + +std::set splitStringToSet(const std::string& str, + const std::string& delimiter); } #endif diff --git a/libfaasm/faasm/state.h b/libfaasm/faasm/state.h index c532801..da231d5 100644 --- a/libfaasm/faasm/state.h +++ b/libfaasm/faasm/state.h @@ -1,7 +1,12 @@ #ifndef FAASM_STATE_H #define FAASM_STATE_H +#include +#include #include +#include +#include +#include #define BIT_MASK_8 0b11111111 #define BIT_MASK_32 0b11111111111111111111111111111111 @@ -10,6 +15,10 @@ namespace faasm { void maskDouble(unsigned int* maskArray, unsigned long idx); void zeroState(const char* key, size_t stateLen); + +// rerturn +std::pair, std::map>> +getPartitionedStates(const std::vector& todoKeys); } // namespace faasm #endif diff --git a/libfaasm/input.cpp b/libfaasm/input.cpp index 9a96f54..7b448e9 100644 --- a/libfaasm/input.cpp +++ b/libfaasm/input.cpp @@ -1,5 +1,6 @@ #include "faasm/input.h" #include "faasm/core.h" +#include "faasm/serialization.h" #include #include @@ -38,6 +39,17 @@ const std::vector getInputVec() return inputBuffer; } +const std::map> getInputMap() +{ + // get the inputMap (inputdata) + std::vector vec = getInputVec(); + + size_t index = 0; // Reset index if reusing buffer + auto inputMap = faasm::deserializeNestedMap(vec, index); + + return inputMap; +} + int getIntInput() { const char* inputStr = faasm::getStringInput("0"); @@ -90,4 +102,31 @@ const std::string concatInput(const std::vector& input) return result; } +// Function to split a string by a delimiter and store the elements in a set +std::set splitStringToSet(const std::string& str, + const std::string& delimiter) +{ + std::set resultSet; + std::size_t start = 0; + std::size_t end; + std::size_t delimiter_length = delimiter.length(); + + while ((end = str.find(delimiter, start)) != std::string::npos) { + std::string token = str.substr(start, end - start); + if (!token.empty()) { + resultSet.insert(std::move(token)); + } + start = end + delimiter_length; + } + + // Add the last token if it's not empty + std::string token = str.substr(start); + if (!token.empty()) { + resultSet.insert(std::move(token)); + } + + // Return the set using std::move to avoid reconstruction + return std::move(resultSet); +} + } // namespace faasm diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index 9448fab..a2e6b7a 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -60,4 +60,5 @@ __faasm_read_partitioned_function_state __faasm_write_partitioned_function_state_unlock __faasm_read_indiv_function_state_size_lock __faasm_read_indiv_function_state -__faasm_write_indiv_function_state_unlock \ No newline at end of file +__faasm_write_indiv_function_state_unlock +__faasm_set_output_id \ No newline at end of file diff --git a/libfaasm/state.cpp b/libfaasm/state.cpp index 6ba21e4..7b9b231 100644 --- a/libfaasm/state.cpp +++ b/libfaasm/state.cpp @@ -1,5 +1,8 @@ #include "faasm/state.h" #include "faasm/core.h" +#include "faasm/input.h" +#include "faasm/serialization.h" + #include namespace faasm { @@ -18,4 +21,39 @@ void zeroState(const char* key, size_t stateLen) faasmWriteState(key, arr, stateLen); faasmPushState(key); } + +std::pair, std::map>> +getPartitionedStates(const std::vector& todoKeys) +{ + std::string todoKeysStr = faasm::concatInput(todoKeys); + + // Prepare the memory space for locked keys + int lockedKeysSize = todoKeysStr.size() + 1; + auto lockedKeys = new uint8_t[lockedKeysSize]; + + // Read and lock the state size + size_t readSize = + faasmReadIndivFunctionStateSizeLock(todoKeysStr.c_str(), lockedKeys); + + // Get the Locked Keys + std::string lockedKeysStr(reinterpret_cast(lockedKeys)); + delete[] lockedKeys; // Clean up allocated memory + auto lockedKeysSet = faasm::splitStringToSet(lockedKeysStr, "|"); + + // Initialize the partitioned state map + std::map> partitionedState; + if (readSize != 0) { + // Read the state and deserialize it + std::vector stateBuffer(readSize); + faasmReadIndivFunctionState( + stateBuffer.data(), readSize, lockedKeysStr.c_str()); + partitionedState = faasm::deserializeParState(stateBuffer); + } + + // Return both the locked keys string and the partitioned state using + // std::move to avoid reconstruction + return std::make_pair(std::move(lockedKeysSet), + std::move(partitionedState)); +} + } From c68878cd5c9c89475278b182bb9e7806f00801d8 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Thu, 5 Sep 2024 00:01:27 +0000 Subject: [PATCH 18/23] feat(stream): Add time monitoring As title. --- func/stream/CMakeLists.txt | 1 + func/stream/mo_alert.cpp | 13 +++++ func/stream/mo_anomaly.cpp | 14 ++++- func/stream/mo_score.cpp | 13 +++++ func/stream/sd_moving_avg.cpp | 12 +++++ func/stream/stateful_exp.cpp | 78 ++++++++++++++++++++++++++++ func/stream/wordcountindiv_count.cpp | 14 ++++- libfaasm/core.cpp | 5 ++ libfaasm/faasm/core.h | 2 + libfaasm/faasm/host_interface.h | 2 + libfaasm/faasm/time.h | 3 ++ libfaasm/libfaasm.imports | 3 +- libfaasm/time.cpp | 18 +++++++ 13 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 func/stream/stateful_exp.cpp diff --git a/func/stream/CMakeLists.txt b/func/stream/CMakeLists.txt index a0a2ae7..33e87f2 100644 --- a/func/stream/CMakeLists.txt +++ b/func/stream/CMakeLists.txt @@ -25,6 +25,7 @@ stream_func(sd_spike_detect sd_spike_detect.cpp) stream_func(mo_alert mo_alert.cpp) stream_func(mo_anomaly mo_anomaly.cpp) stream_func(mo_score mo_score.cpp) +stream_func(stateful_exp stateful_exp.cpp) # Custom target to group all the stream functions add_custom_target(stream_all_funcs DEPENDS ${ALL_STREAM_FUNCS}) diff --git a/func/stream/mo_alert.cpp b/func/stream/mo_alert.cpp index 728a55d..8fca895 100644 --- a/func/stream/mo_alert.cpp +++ b/func/stream/mo_alert.cpp @@ -197,6 +197,8 @@ int main(int argc, char* argv[]) // get the inputMap (inputdata) auto inputMap = faasm::getInputMap(); + uint64_t start = faasmGetMicros(); + // Get the Function size_t readSize = faasmReadFunctionStateSizeLock(); long prevTimestamp; @@ -314,5 +316,16 @@ int main(int argc, char* argv[]) // std::cout << oss4.str() << std::endl; std::vector stateBytes = serialize(dataTuple); faasmWriteFunctionStateUnlock(stateBytes.data(), stateBytes.size()); + + uint64_t end = faasmGetMicros(); + uint64_t diff = end - start; + // Print start, end, and duration in microseconds + + std::string output = + "mo_alert_duration:" + std::to_string(diff); + for (size_t i = 0; i < inputMap.size(); i++) { + faasmSetOutputId(output.c_str(), output.size(), 0); + } + return 0; } diff --git a/func/stream/mo_anomaly.cpp b/func/stream/mo_anomaly.cpp index 56c1175..845055e 100644 --- a/func/stream/mo_anomaly.cpp +++ b/func/stream/mo_anomaly.cpp @@ -72,6 +72,8 @@ int main(int argc, char* argv[]) // } // std::cout << oss.str() << std::endl; + uint64_t start = faasmGetMicros(); + while (todoKeysMap.size() > 0) { // Collect todoKeys to string std::vector todoKeys; @@ -95,7 +97,7 @@ int main(int argc, char* argv[]) long timestamp = std::get<2>(todoData); pastScores.push_back(score); - if (pastScores.size() == windowlength) { + if (pastScores.size() > windowlength) { pastScores.pop_front(); } @@ -144,6 +146,16 @@ int main(int argc, char* argv[]) partitionedStateBytes.size()); } + uint64_t end = faasmGetMicros(); + uint64_t diff = end - start; + // Print start, end, and duration in microseconds + + std::string output = + "mo_score_anomaly:" + std::to_string(diff); + for (size_t i = 0; i < inputMap.size(); i++) { + faasmSetOutputId(output.c_str(), output.size(), 0); + } + faasmChainInvoke(); return 0; } diff --git a/func/stream/mo_score.cpp b/func/stream/mo_score.cpp index 1f3560c..be8e997 100644 --- a/func/stream/mo_score.cpp +++ b/func/stream/mo_score.cpp @@ -192,6 +192,9 @@ int main(int argc, char* argv[]) // get the inputMap (inputdata) auto inputMap = faasm::getInputMap(); + uint64_t start = faasmGetMicros(); + + // Get and initialize the Function size_t readSize = faasmReadFunctionStateSizeLock(); std::vector> observationList; @@ -272,6 +275,16 @@ int main(int argc, char* argv[]) std::vector stateBytes = serialize(observationList, prevTimestamp); faasmWriteFunctionStateUnlock(stateBytes.data(), stateBytes.size()); + uint64_t end = faasmGetMicros(); + uint64_t diff = end - start; + // Print start, end, and duration in microseconds + + std::string output = + "mo_score_duration:" + std::to_string(diff); + for (size_t i = 0; i < inputMap.size(); i++) { + faasmSetOutputId(output.c_str(), output.size(), 0); + } + faasmChainInvoke(); return 0; } diff --git a/func/stream/sd_moving_avg.cpp b/func/stream/sd_moving_avg.cpp index 9de70e0..f4a564c 100644 --- a/func/stream/sd_moving_avg.cpp +++ b/func/stream/sd_moving_avg.cpp @@ -83,6 +83,8 @@ int main(int argc, char* argv[]) std::tuple(i, inputData)); } + uint64_t start = faasmGetMicros(); + while (todoKeysMap.size() > 0) { // Collect todoKeys to string std::vector todoKeys; @@ -157,6 +159,16 @@ int main(int argc, char* argv[]) partitionedStateBytes.size()); } + uint64_t end = faasmGetMicros(); + uint64_t diff = end - start; + // Print start, end, and duration in microseconds + + std::string output = + "mo_moving_avg_duration:" + std::to_string(diff); + for (size_t i = 0; i < inputMap.size(); i++) { + faasmSetOutputId(output.c_str(), output.size(), 0); + } + faasmChainInvoke(); return 0; diff --git a/func/stream/stateful_exp.cpp b/func/stream/stateful_exp.cpp new file mode 100644 index 0000000..c651b04 --- /dev/null +++ b/func/stream/stateful_exp.cpp @@ -0,0 +1,78 @@ +#include "faasm/input.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// We must register the function_state in scheduler! +std::vector serialize(int value) { + std::vector serializedData(sizeof(int)); + for (size_t i = 0; i < sizeof(int); ++i) { + serializedData[i] = (value >> (i * 8)) & 0xFF; + } + return serializedData; +} + +int deserialize(const std::vector& data) { + int value = 0; + for (size_t i = 0; i < sizeof(int); ++i) { + value |= (static_cast(data[i]) << (i * 8)); + } + return value; +} + +int main(int argc, char* argv[]) +{ + uint64_t start = faasmGetMicros(); + // get the inputMap (inputdata) + auto inputMap = faasm::getInputMap(); + + // Get and initialize the Function + size_t readSize = faasmReadFunctionStateSizeLock(); + int count; + if (readSize == 0) { + long count = 0; + } else { + std::vector stateBytes(readSize); + faasmReadFunctionState(stateBytes.data(), readSize); + count = deserialize(stateBytes); + } + + // std::cout << "input size is : " << inputMap.size() << std::endl; + // Process each request + for (int i = 0; i < inputMap.size(); i++) { + count++; + // std::cout << "count is : " << count << std::endl; + int simulator = 0; + for (int j = 0; j < 100000; j++) { + simulator += j * i; + simulator += i; + simulator = simulator % (i + j +1) + 1; + } + printf("simulator is : %d\n", simulator); + } + + std::vector stateBytes = serialize(count); + faasmWriteFunctionStateUnlock(stateBytes.data(), stateBytes.size()); + // Record the end time + uint64_t end = faasmGetMicros(); + uint64_t diff = end - start; + // Print start, end, and duration in microseconds + std::string output = "stateful_exp_duration:" + std::to_string(diff); + faasmSetOutputId(output.c_str(), output.size(), 0); + return 0; +} diff --git a/func/stream/wordcountindiv_count.cpp b/func/stream/wordcountindiv_count.cpp index 1434ddd..56ff634 100644 --- a/func/stream/wordcountindiv_count.cpp +++ b/func/stream/wordcountindiv_count.cpp @@ -54,7 +54,7 @@ int main(int argc, char* argv[]) for (size_t i = 0; i < inputMap.size(); i++) { // get the input for this spefic function invoke. std::string inputParStr = - inputMap[std::to_string(i)]["parititonedAttribute"]; + inputMap[std::to_string(i)]["partitionedAttribute"]; if (todoKeysMap.find(inputParStr) != todoKeysMap.end()) { todoKeysMap[inputParStr]++; } else { @@ -62,6 +62,8 @@ int main(int argc, char* argv[]) } } + uint64_t start = faasmGetMicros(); + // BEGIN the loop while (todoKeysMap.size() > 0) { todoKeys.clear(); @@ -109,6 +111,16 @@ int main(int argc, char* argv[]) partitionedStateBytes.size()); } + uint64_t end = faasmGetMicros(); + uint64_t diff = end - start; + // Print start, end, and duration in microseconds + + std::string output = + "wordcount_count_lock_duration:" + std::to_string(diff); + for (size_t i = 0; i < inputMap.size(); i++) { + faasmSetOutputId(output.c_str(), output.size(), 0); + } + // printf("finished"); return 0; diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index 30dc69a..1311cdd 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -365,3 +365,8 @@ void faasmChainInvoke() void faasmSetOutputId(const char* newOutput, long outputLen, int idx){ __faasm_set_output_id(newOutput, outputLen, idx); } + +// Get the current time in microseconds +uint64_t faasmGetMicros(){ + return __faasm_get_micros(); +} diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index b2657a5..d422a51 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -322,6 +322,8 @@ extern "C" void faasmFunctionStateUnlock(); void faasmChainInvoke(); + + uint64_t faasmGetMicros(); /** * Sets the given string as the output data for this function of specified diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index a5e5b00..786e7a0 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -226,4 +226,6 @@ void __faasm_write_indiv_function_state_unlock(const uint8_t* data, HOST_IFACE_FUNC void __faasm_set_output_id(const char* newOutput, long outputLen, int idx); +HOST_IFACE_FUNC +uint64_t __faasm_get_micros(); #endif \ No newline at end of file diff --git a/libfaasm/faasm/time.h b/libfaasm/faasm/time.h index 46e1502..074a072 100644 --- a/libfaasm/faasm/time.h +++ b/libfaasm/faasm/time.h @@ -3,6 +3,7 @@ #include #include +#include // Turn on timings when not building wasm or when WASM_PROF requested #if WASM_PROF == 1 || __wasm__ != 1 @@ -17,6 +18,8 @@ namespace faasm { double getSecondsSinceEpoch(); +double getMillisSinceEpoch(); +int64_t getNanosecondsSinceEpoch(); } #endif diff --git a/libfaasm/libfaasm.imports b/libfaasm/libfaasm.imports index a2e6b7a..b745999 100644 --- a/libfaasm/libfaasm.imports +++ b/libfaasm/libfaasm.imports @@ -61,4 +61,5 @@ __faasm_write_partitioned_function_state_unlock __faasm_read_indiv_function_state_size_lock __faasm_read_indiv_function_state __faasm_write_indiv_function_state_unlock -__faasm_set_output_id \ No newline at end of file +__faasm_set_output_id +__faasm_get_micros \ No newline at end of file diff --git a/libfaasm/time.cpp b/libfaasm/time.cpp index df7a7b6..99c0eb2 100644 --- a/libfaasm/time.cpp +++ b/libfaasm/time.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace faasm { double getSecondsSinceEpoch() @@ -12,4 +13,21 @@ double getSecondsSinceEpoch() double secs = tp.tv_sec + (tp.tv_usec / 1e6); return secs; } + +double getMillisSinceEpoch() +{ + struct timeval tp; + gettimeofday(&tp, NULL); + + // Calculate milliseconds since epoch + double millis = (tp.tv_sec * 1000.0) + (tp.tv_usec / 1000.0); + return millis; +} + +int64_t getNanosecondsSinceEpoch() { + auto now = std::chrono::system_clock::now(); + auto duration = std::chrono::duration_cast(now.time_since_epoch()); + return duration.count(); // This is int64_t (long long) +} + } // namespace faasm From 6629a57bc8cc8b21897e784e2ff58849128e0335 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Thu, 5 Sep 2024 00:29:22 +0000 Subject: [PATCH 19/23] fix(mo_anomaly): lock duration bug As title. --- func/stream/mo_anomaly.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/func/stream/mo_anomaly.cpp b/func/stream/mo_anomaly.cpp index 845055e..10d6748 100644 --- a/func/stream/mo_anomaly.cpp +++ b/func/stream/mo_anomaly.cpp @@ -151,7 +151,7 @@ int main(int argc, char* argv[]) // Print start, end, and duration in microseconds std::string output = - "mo_score_anomaly:" + std::to_string(diff); + "mo_score_anomaly_duration:" + std::to_string(diff); for (size_t i = 0; i < inputMap.size(); i++) { faasmSetOutputId(output.c_str(), output.size(), 0); } From 5e9cfb8e4b5bfbf838b6f859bf595ebd242bfd85 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Thu, 5 Sep 2024 01:52:51 +0000 Subject: [PATCH 20/23] fix(stream): Add input size output As title. --- func/stream/mo_alert.cpp | 5 +++-- func/stream/mo_anomaly.cpp | 5 +++-- func/stream/mo_score.cpp | 5 +++-- func/stream/sd_moving_avg.cpp | 7 ++++--- func/stream/wordcountindiv_count.cpp | 6 +++--- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/func/stream/mo_alert.cpp b/func/stream/mo_alert.cpp index 8fca895..35ca91f 100644 --- a/func/stream/mo_alert.cpp +++ b/func/stream/mo_alert.cpp @@ -321,10 +321,11 @@ int main(int argc, char* argv[]) uint64_t diff = end - start; // Print start, end, and duration in microseconds + int inputSize = inputMap.size(); std::string output = - "mo_alert_duration:" + std::to_string(diff); + "mo_alert_input_size: " + std::to_string(inputSize) + " and duration:" + std::to_string(diff); for (size_t i = 0; i < inputMap.size(); i++) { - faasmSetOutputId(output.c_str(), output.size(), 0); + faasmSetOutputId(output.c_str(), output.size(), i); } return 0; diff --git a/func/stream/mo_anomaly.cpp b/func/stream/mo_anomaly.cpp index 10d6748..8cfbd4a 100644 --- a/func/stream/mo_anomaly.cpp +++ b/func/stream/mo_anomaly.cpp @@ -150,10 +150,11 @@ int main(int argc, char* argv[]) uint64_t diff = end - start; // Print start, end, and duration in microseconds + int inputSize = inputMap.size(); std::string output = - "mo_score_anomaly_duration:" + std::to_string(diff); + "mo_anomaly_input_size: " + std::to_string(inputSize) + " and duration:" + std::to_string(diff); for (size_t i = 0; i < inputMap.size(); i++) { - faasmSetOutputId(output.c_str(), output.size(), 0); + faasmSetOutputId(output.c_str(), output.size(), i); } faasmChainInvoke(); diff --git a/func/stream/mo_score.cpp b/func/stream/mo_score.cpp index be8e997..e30b780 100644 --- a/func/stream/mo_score.cpp +++ b/func/stream/mo_score.cpp @@ -279,10 +279,11 @@ int main(int argc, char* argv[]) uint64_t diff = end - start; // Print start, end, and duration in microseconds + int inputSize = inputMap.size(); std::string output = - "mo_score_duration:" + std::to_string(diff); + "mo_score_input_size: " + std::to_string(inputSize) + " and duration:" + std::to_string(diff); for (size_t i = 0; i < inputMap.size(); i++) { - faasmSetOutputId(output.c_str(), output.size(), 0); + faasmSetOutputId(output.c_str(), output.size(), i); } faasmChainInvoke(); diff --git a/func/stream/sd_moving_avg.cpp b/func/stream/sd_moving_avg.cpp index f4a564c..b92ec00 100644 --- a/func/stream/sd_moving_avg.cpp +++ b/func/stream/sd_moving_avg.cpp @@ -162,11 +162,12 @@ int main(int argc, char* argv[]) uint64_t end = faasmGetMicros(); uint64_t diff = end - start; // Print start, end, and duration in microseconds - + + int inputSize = inputMap.size(); std::string output = - "mo_moving_avg_duration:" + std::to_string(diff); + "mo_moving_avg_input_size: " + std::to_string(inputSize) + " and duration:" + std::to_string(diff); for (size_t i = 0; i < inputMap.size(); i++) { - faasmSetOutputId(output.c_str(), output.size(), 0); + faasmSetOutputId(output.c_str(), output.size(), i); } faasmChainInvoke(); diff --git a/func/stream/wordcountindiv_count.cpp b/func/stream/wordcountindiv_count.cpp index 56ff634..d21c584 100644 --- a/func/stream/wordcountindiv_count.cpp +++ b/func/stream/wordcountindiv_count.cpp @@ -115,12 +115,12 @@ int main(int argc, char* argv[]) uint64_t diff = end - start; // Print start, end, and duration in microseconds + int inputSize = inputMap.size(); std::string output = - "wordcount_count_lock_duration:" + std::to_string(diff); + "wordcount_count_lock_input_size: " + std::to_string(inputSize) + " and duration:" + std::to_string(diff); for (size_t i = 0; i < inputMap.size(); i++) { - faasmSetOutputId(output.c_str(), output.size(), 0); + faasmSetOutputId(output.c_str(), output.size(), i); } - // printf("finished"); return 0; From 5d2d394ea445f52c8e37c314b433ea19369e3f8d Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Mon, 9 Sep 2024 23:52:10 +0000 Subject: [PATCH 21/23] feat(sd): remove unnecessary logs As title. --- func/stream/sd_moving_avg.cpp | 32 ++++++++++++++++---------------- func/stream/sd_spike_detect.cpp | 12 ++++++------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/func/stream/sd_moving_avg.cpp b/func/stream/sd_moving_avg.cpp index b92ec00..aa638e1 100644 --- a/func/stream/sd_moving_avg.cpp +++ b/func/stream/sd_moving_avg.cpp @@ -131,22 +131,22 @@ int main(int argc, char* argv[]) idx); } // Print : used for testing - { - std::string valuesStr = "["; - for (auto it = values.begin(); it != values.end(); ++it) { - valuesStr += std::to_string(*it); - if (std::next(it) != values.end()) { - valuesStr += ", "; - } - } - valuesStr += "]"; - - // Print out statistics including the values in one line - std::cout << "Key: " << key << ", Count: " << values.size() - << ", Sum: " << sum - << ", Average: " << (sum / values.size()) - << ", Values: " << valuesStr << std::endl; - } + // { + // std::string valuesStr = "["; + // for (auto it = values.begin(); it != values.end(); ++it) { + // valuesStr += std::to_string(*it); + // if (std::next(it) != values.end()) { + // valuesStr += ", "; + // } + // } + // valuesStr += "]"; + + // // Print out statistics including the values in one line + // std::cout << "Key: " << key << ", Count: " << values.size() + // << ", Sum: " << sum + // << ", Average: " << (sum / values.size()) + // << ", Values: " << valuesStr << std::endl; + // } std::pair> newStatistics = { sum, values }; std::vector newStatisticsBytes = serialize(newStatistics); diff --git a/func/stream/sd_spike_detect.cpp b/func/stream/sd_spike_detect.cpp index 1d3d111..658947b 100644 --- a/func/stream/sd_spike_detect.cpp +++ b/func/stream/sd_spike_detect.cpp @@ -24,15 +24,15 @@ int main(int argc, char* argv[]) spikeThreshold * movingAverage) { std::string output = "detected spike"; faasmSetOutputId(output.c_str(), output.size(), i); - std::cout << "Input " << i << ": Moving Average = " << movingAverage - << ", Temperature = " << temperature - << ", Status = Detected" << std::endl; + // std::cout << "Input " << i << ": Moving Average = " << movingAverage + // << ", Temperature = " << temperature + // << ", Status = Detected" << std::endl; } else { std::string output = "no spike"; faasmSetOutputId(output.c_str(), output.size(), i); - std::cout << "Input " << i << ": Moving Average = " << movingAverage - << ", Temperature = " << temperature - << ", Status = Undetected" << std::endl; + // std::cout << "Input " << i << ": Moving Average = " << movingAverage + // << ", Temperature = " << temperature + // << ", Status = Undetected" << std::endl; } } From f8a4947302e1aca203abcc3e99ea3225232968e9 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Wed, 11 Sep 2024 01:04:16 +0000 Subject: [PATCH 22/23] feat(wc): Remove print Remove print statement from wordcount individual count function. --- func/stream/wordcountindiv_count.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/func/stream/wordcountindiv_count.cpp b/func/stream/wordcountindiv_count.cpp index d21c584..86a4c9a 100644 --- a/func/stream/wordcountindiv_count.cpp +++ b/func/stream/wordcountindiv_count.cpp @@ -99,12 +99,12 @@ int main(int argc, char* argv[]) partitionedState[key] = faasm::uint32ToUint8V(count); } // write data back - for (const auto& pair : partitionedState) { - std::cout << pair.first << ": "; - int count = faasm::uint8VToUint32(pair.second); - std::cout << count; - std::cout << std::endl; - } + // for (const auto& pair : partitionedState) { + // std::cout << pair.first << ": "; + // int count = faasm::uint8VToUint32(pair.second); + // std::cout << count; + // std::cout << std::endl; + // } std::vector partitionedStateBytes = faasm::serializeParState(partitionedState); faasmWriteIndivFunctionStateUnlock(partitionedStateBytes.data(), From fd94ea8f8455554e99978200ce2df9212f998301 Mon Sep 17 00:00:00 2001 From: Tianyu Qi Date: Fri, 1 Nov 2024 08:43:49 +0000 Subject: [PATCH 23/23] chore(libfaasm): remove unused code As title. --- libfaasm/core.cpp | 10 ---------- libfaasm/faasm/core.h | 4 ---- libfaasm/faasm/host_interface.h | 5 ----- 3 files changed, 19 deletions(-) diff --git a/libfaasm/core.cpp b/libfaasm/core.cpp index 1311cdd..837fe1a 100644 --- a/libfaasm/core.cpp +++ b/libfaasm/core.cpp @@ -347,16 +347,6 @@ void faasmWriteFunctionStateUnlock(const uint8_t* data, long dataLen) __faasm_write_function_state_unlock(data, dataLen); } -long faasmFunctionStateLock() -{ - return __faasm_function_state_lock(); -} - -void faasmFunctionStateUnlock() -{ - __faasm_function_state_unlock(); -} - void faasmChainInvoke() { __faasm_chain_invoke(); diff --git a/libfaasm/faasm/core.h b/libfaasm/faasm/core.h index d422a51..47db24d 100644 --- a/libfaasm/faasm/core.h +++ b/libfaasm/faasm/core.h @@ -317,10 +317,6 @@ extern "C" void faasmWriteIndivFunctionStateUnlock(const uint8_t* data, long dataLen); - long faasmFunctionStateLock(); - - void faasmFunctionStateUnlock(); - void faasmChainInvoke(); uint64_t faasmGetMicros(); diff --git a/libfaasm/faasm/host_interface.h b/libfaasm/faasm/host_interface.h index 786e7a0..20cba3a 100644 --- a/libfaasm/faasm/host_interface.h +++ b/libfaasm/faasm/host_interface.h @@ -185,11 +185,6 @@ HOST_IFACE_FUNC void __faasm_write_function_state_unlock(const unsigned char* data, long dataLen); -HOST_IFACE_FUNC -long __faasm_function_state_lock(); - -HOST_IFACE_FUNC -void __faasm_function_state_unlock(); // lock == 0 means false, lock == 1 means true. HOST_IFACE_FUNC