[clang-tools-extra] 466acd6 - [clangd] Avoid reallocating buffers for each message read:

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 13 08:40:54 PST 2021


Author: Sam McCall
Date: 2021-01-13T17:40:33+01:00
New Revision: 466acd694861138997d668a3f9cb29aa87bd316e

URL: https://github.com/llvm/llvm-project/commit/466acd694861138997d668a3f9cb29aa87bd316e
DIFF: https://github.com/llvm/llvm-project/commit/466acd694861138997d668a3f9cb29aa87bd316e.diff

LOG: [clangd] Avoid reallocating buffers for each message read:

 - reuse std::string we read messages into
 - when reading line-wise, use SmallVector<128> and read in chunks of 128
   (this affects headers, which are short, and tests, which don't matter)

Differential Revision: https://reviews.llvm.org/D93653

Added: 
    

Modified: 
    clang-tools-extra/clangd/JSONTransport.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/JSONTransport.cpp b/clang-tools-extra/clangd/JSONTransport.cpp
index 662e5df4e27b..3e8caceda21c 100644
--- a/clang-tools-extra/clangd/JSONTransport.cpp
+++ b/clang-tools-extra/clangd/JSONTransport.cpp
@@ -10,6 +10,7 @@
 #include "support/Cancellation.h"
 #include "support/Logger.h"
 #include "support/Shutdown.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/Support/Errno.h"
 #include "llvm/Support/Error.h"
 #include <system_error>
@@ -99,6 +100,7 @@ class JSONTransport : public Transport {
   }
 
   llvm::Error loop(MessageHandler &Handler) override {
+    std::string JSON; // Messages may be large, reuse same big buffer.
     while (!feof(In)) {
       if (shutdownRequested())
         return error(std::make_error_code(std::errc::operation_canceled),
@@ -106,14 +108,14 @@ class JSONTransport : public Transport {
       if (ferror(In))
         return llvm::errorCodeToError(
             std::error_code(errno, std::system_category()));
-      if (auto JSON = readRawMessage()) {
-        if (auto Doc = llvm::json::parse(*JSON)) {
+      if (readRawMessage(JSON)) {
+        if (auto Doc = llvm::json::parse(JSON)) {
           vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
           if (!handleMessage(std::move(*Doc), Handler))
             return llvm::Error::success(); // we saw the "exit" notification.
         } else {
           // Parse error. Log the raw message.
-          vlog("<<< {0}\n", *JSON);
+          vlog("<<< {0}\n", JSON);
           elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
         }
       }
@@ -136,12 +138,12 @@ class JSONTransport : public Transport {
   }
 
   // Read raw string messages from input stream.
-  llvm::Optional<std::string> readRawMessage() {
-    return Style == JSONStreamStyle::Delimited ? readDelimitedMessage()
-                                               : readStandardMessage();
+  bool readRawMessage(std::string &JSON) {
+    return Style == JSONStreamStyle::Delimited ? readDelimitedMessage(JSON)
+                                               : readStandardMessage(JSON);
   }
-  llvm::Optional<std::string> readDelimitedMessage();
-  llvm::Optional<std::string> readStandardMessage();
+  bool readDelimitedMessage(std::string &JSON);
+  bool readStandardMessage(std::string &JSON);
 
   llvm::SmallVector<char, 0> OutputBuffer;
   std::FILE *In;
@@ -191,12 +193,14 @@ bool JSONTransport::handleMessage(llvm::json::Value Message,
 
 // Tries to read a line up to and including \n.
 // If failing, feof(), ferror(), or shutdownRequested() will be set.
-bool readLine(std::FILE *In, std::string &Out) {
-  static constexpr int BufSize = 1024;
+bool readLine(std::FILE *In, llvm::SmallVectorImpl<char> &Out) {
+  // Big enough to hold any reasonable header line. May not fit content lines
+  // in delimited mode, but performance doesn't matter for that mode.
+  static constexpr int BufSize = 128;
   size_t Size = 0;
   Out.clear();
   for (;;) {
-    Out.resize(Size + BufSize);
+    Out.resize_for_overwrite(Size + BufSize);
     // Handle EINTR which is sent when a debugger attaches on some platforms.
     if (!retryAfterSignalUnlessShutdown(
             nullptr, [&] { return std::fgets(&Out[Size], BufSize, In); }))
@@ -216,14 +220,14 @@ bool readLine(std::FILE *In, std::string &Out) {
 // Returns None when:
 //  - ferror(), feof(), or shutdownRequested() are set.
 //  - Content-Length is missing or empty (protocol error)
-llvm::Optional<std::string> JSONTransport::readStandardMessage() {
+bool JSONTransport::readStandardMessage(std::string &JSON) {
   // A Language Server Protocol message starts with a set of HTTP headers,
   // delimited  by \r\n, and terminated by an empty line (\r\n).
   unsigned long long ContentLength = 0;
-  std::string Line;
+  llvm::SmallString<128> Line;
   while (true) {
     if (feof(In) || ferror(In) || !readLine(In, Line))
-      return llvm::None;
+      return false;
     InMirror << Line;
 
     llvm::StringRef LineRef(Line);
@@ -258,14 +262,14 @@ llvm::Optional<std::string> JSONTransport::readStandardMessage() {
     elog("Refusing to read message with long Content-Length: {0}. "
          "Expect protocol errors",
          ContentLength);
-    return llvm::None;
+    return false;
   }
   if (ContentLength == 0) {
     log("Warning: Missing Content-Length header, or zero-length message.");
-    return llvm::None;
+    return false;
   }
 
-  std::string JSON(ContentLength, '\0');
+  JSON.resize(ContentLength);
   for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
     // Handle EINTR which is sent when a debugger attaches on some platforms.
     Read = retryAfterSignalUnlessShutdown(0, [&]{
@@ -274,24 +278,24 @@ llvm::Optional<std::string> JSONTransport::readStandardMessage() {
     if (Read == 0) {
       elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
            ContentLength);
-      return llvm::None;
+      return false;
     }
     InMirror << llvm::StringRef(&JSON[Pos], Read);
     clearerr(In); // If we're done, the error was transient. If we're not done,
                   // either it was transient or we'll see it again on retry.
     Pos += Read;
   }
-  return std::move(JSON);
+  return true;
 }
 
 // For lit tests we support a simplified syntax:
 // - messages are delimited by '---' on a line by itself
 // - lines starting with # are ignored.
 // This is a testing path, so favor simplicity over performance here.
-// When returning None, feof(), ferror(), or shutdownRequested() will be set.
-llvm::Optional<std::string> JSONTransport::readDelimitedMessage() {
-  std::string JSON;
-  std::string Line;
+// When returning false: feof(), ferror(), or shutdownRequested() will be set.
+bool JSONTransport::readDelimitedMessage(std::string &JSON) {
+  JSON.clear();
+  llvm::SmallString<128> Line;
   while (readLine(In, Line)) {
     InMirror << Line;
     auto LineRef = llvm::StringRef(Line).trim();
@@ -306,12 +310,12 @@ llvm::Optional<std::string> JSONTransport::readDelimitedMessage() {
   }
 
   if (shutdownRequested())
-    return llvm::None;
+    return false;
   if (ferror(In)) {
     elog("Input error while reading message!");
-    return llvm::None;
+    return false;
   }
-  return std::move(JSON); // Including at EOF
+  return true; // Including at EOF
 }
 
 } // namespace


        


More information about the cfe-commits mailing list