From 64062cab436d89d7e00bb62619856fe666c9f7fe Mon Sep 17 00:00:00 2001 From: Mark Williams Date: Mon, 23 Apr 2018 08:53:15 -0700 Subject: [PATCH] Don't use c++ streams for jit serialization Summary: C++ streams can be slow; they're also overkill for what we're doing. In addition, we want to be able to serialize the jit state while crashing, and apart from one hash-table that we build during serialization (which we can probably eliminate), this was the only thing thats not signal handler safe. Reviewed By: alexeyt Differential Revision: D7719797 fbshipit-source-id: 2d64c0874115c5b5a44eb27a32902acff6056e7b --- hphp/runtime/vm/jit/prof-data-serialize.cpp | 81 ++++++++++++++++++++++++++--- hphp/runtime/vm/jit/prof-data-serialize.h | 17 ++++-- 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/hphp/runtime/vm/jit/prof-data-serialize.cpp b/hphp/runtime/vm/jit/prof-data-serialize.cpp index d36bef122bb..608d25d0672 100644 --- a/hphp/runtime/vm/jit/prof-data-serialize.cpp +++ b/hphp/runtime/vm/jit/prof-data-serialize.cpp @@ -43,7 +43,7 @@ #include "hphp/util/build-info.h" -#include +#include namespace HPHP { namespace jit { ////////////////////////////////////////////////////////////////////// @@ -549,25 +549,89 @@ void read_target_profiles(ProfDataDeserializer& ser) { //////////////////////////////////////////////////////////////////////////////// } -ProfDataSerializer::ProfDataSerializer(const std::string& name) : m_ofs(name) { - if (!m_ofs.good()) throw std::runtime_error("Failed to open: " + name); +ProfDataSerializer::ProfDataSerializer(const std::string& name) { + fd = open(name.c_str(), O_CLOEXEC | O_CREAT | O_TRUNC | O_WRONLY, 0644); + if (fd == -1) throw std::runtime_error("Failed to open: " + name); } -ProfDataDeserializer::ProfDataDeserializer(const std::string& name) - : m_ifs(name) { - if (!m_ifs.good()) throw std::runtime_error("Failed to open: " + name); +ProfDataSerializer::~ProfDataSerializer() { + assertx(fd != -1); + if (offset) ::write(fd, buffer, offset); + close(fd); +} + +ProfDataDeserializer::ProfDataDeserializer(const std::string& name) { + fd = open(name.c_str(), O_CLOEXEC | O_RDONLY); + if (fd == -1) throw std::runtime_error("Failed to open: " + name); +} + +ProfDataDeserializer::~ProfDataDeserializer() { + assertx(fd != -1); + close(fd); +} + +bool ProfDataDeserializer::done() { + char byte; + return offset == buffer_size && ::read(fd, &byte, 1) == 0; } void write_raw(ProfDataSerializer& ser, const void* data, size_t sz) { - if (!ser.m_ofs.write(static_cast(data), sz).good()) { + if (ser.offset + sz <= ProfDataSerializer::buffer_size) { + memcpy(ser.buffer + ser.offset, data, sz); + ser.offset += sz; + return; + } + if (ser.offset == 0) { + if (::write(ser.fd, data, sz) != sz) { + throw std::runtime_error("Failed to write serialized data"); + } + return; + } + if (auto const delta = ProfDataSerializer::buffer_size - ser.offset) { + memcpy(ser.buffer + ser.offset, data, delta); + data = static_cast(data) + delta; + sz -= delta; + ser.offset = ProfDataSerializer::buffer_size; + } + assertx(ser.offset == ProfDataSerializer::buffer_size); + if (::write(ser.fd, ser.buffer, ser.offset) != ser.offset) { throw std::runtime_error("Failed to write serialized data"); } + ser.offset = 0; + write_raw(ser, data, sz); } void read_raw(ProfDataDeserializer& ser, void* data, size_t sz) { - if (!ser.m_ifs.read(static_cast(data), sz).good()) { + if (ser.offset + sz <= ProfDataDeserializer::buffer_size) { + memcpy(data, ser.buffer + ser.offset, sz); + ser.offset += sz; + return; + } + if (auto const delta = ProfDataDeserializer::buffer_size - ser.offset) { + memcpy(data, ser.buffer + ser.offset, delta); + data = static_cast(data) + delta; + sz -= delta; + ser.offset = ProfDataDeserializer::buffer_size; + } + if (sz >= ProfDataDeserializer::buffer_size) { + auto const bytes_read = ::read(ser.fd, data, sz); + if (bytes_read < 0 || bytes_read < sz) { + throw std::runtime_error("Failed to read serialized data"); + } + return; + } + + auto const bytes_read = ::read(ser.fd, + ser.buffer, + ProfDataDeserializer::buffer_size); + if (bytes_read < 0 || bytes_read < sz) { throw std::runtime_error("Failed to read serialized data"); } + ser.offset = ProfDataDeserializer::buffer_size - bytes_read; + if (ser.offset) { + memmove(ser.buffer + ser.offset, ser.buffer, bytes_read); + } + return read_raw(ser, data, sz); } StringData*& ProfDataDeserializer::getEnt(const StringData* p) { @@ -963,6 +1027,7 @@ bool deserializeProfData(const std::string& filename) { read_target_profiles(ser); + always_assert(ser.done()); return true; } catch (std::runtime_error& err) { FTRACE(1, "deserializeProfData - Failed: {}\n", err.what()); diff --git a/hphp/runtime/vm/jit/prof-data-serialize.h b/hphp/runtime/vm/jit/prof-data-serialize.h index 192e05672bc..00624f49a98 100644 --- a/hphp/runtime/vm/jit/prof-data-serialize.h +++ b/hphp/runtime/vm/jit/prof-data-serialize.h @@ -18,7 +18,6 @@ #define incl_HPHP_PROF_DATA_SERIALIZE_H_ #include -#include #include #include @@ -39,6 +38,7 @@ namespace jit { struct ProfDataSerializer { explicit ProfDataSerializer(const std::string& name); + ~ProfDataSerializer(); friend void write_raw(ProfDataSerializer& ser, const void* data, size_t sz); @@ -54,7 +54,10 @@ struct ProfDataSerializer { return serializedStatics.emplace(arr).second; } private: - std::ofstream m_ofs; + int fd; + static constexpr uint32_t buffer_size = 8192; + uint32_t offset{0}; + char buffer[buffer_size]; // keep track of which static strings and arrays have already been serialized std::unordered_set serializedStatics; }; @@ -64,6 +67,7 @@ struct ProfDataDeserializer { using EntMap = std::unordered_map; explicit ProfDataDeserializer(const std::string& name); + ~ProfDataDeserializer(); friend void read_raw(ProfDataDeserializer& ser, void* data, size_t sz); @@ -106,8 +110,13 @@ struct ProfDataDeserializer { Func*& getEnt(const Func* p); Class*& getEnt(const Class* p); const RepoAuthType::Array*& getEnt(const RepoAuthType::Array* p); -private: - std::ifstream m_ifs; + + bool done(); + private: + int fd; + static constexpr uint32_t buffer_size = 8192; + uint32_t offset{buffer_size}; + char buffer[buffer_size]; EntMap stringMap; EntMap arrayMap; -- 2.11.4.GIT