Skip to content

Commit

Permalink
Fix time complexity of string replacement
Browse files Browse the repository at this point in the history
This change fixes a bug where replacing text in a very long string could
cause llama.cpp to hang indefinitely. This is because the algorithm used
was quadratic, due to memmove() when s.replace() is called in a loop. It
seems most search results and LLM responses actually provide the O(n**2)
algorithm, which is a great tragedy. Using a builder string fixes things
  • Loading branch information
jart committed Aug 25, 2024
1 parent e11bd85 commit b3c41a8
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 20 deletions.
19 changes: 12 additions & 7 deletions common/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1860,14 +1860,19 @@ std::string string_get_sortable_timestamp() {
}

void string_replace_all(std::string & s, const std::string & search, const std::string & replace) {
if (search.empty()) {
return; // Avoid infinite loop if 'search' is an empty string
}
if (search.empty())
return;
std::string builder;
builder.reserve(s.length());
size_t pos = 0;
while ((pos = s.find(search, pos)) != std::string::npos) {
s.replace(pos, search.length(), replace);
pos += replace.length();
}
size_t last_pos = 0;
while ((pos = s.find(search, last_pos)) != std::string::npos) {
builder.append(s, last_pos, pos - last_pos);
builder.append(replace);
last_pos = pos + search.length();
}
builder.append(s, last_pos, std::string::npos);
s = std::move(builder);
}

void string_process_escapes(std::string & input) {
Expand Down
19 changes: 12 additions & 7 deletions examples/llava/clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,19 @@ static std::string gguf_data_to_str(enum gguf_type type, const void * data, int
}

static void replace_all(std::string & s, const std::string & search, const std::string & replace) {
if (search.empty()) {
return; // Avoid infinite loop if 'search' is an empty string
}
if (search.empty())
return;
std::string builder;
builder.reserve(s.length());
size_t pos = 0;
while ((pos = s.find(search, pos)) != std::string::npos) {
s.replace(pos, search.length(), replace);
pos += replace.length();
}
size_t last_pos = 0;
while ((pos = s.find(search, last_pos)) != std::string::npos) {
builder.append(s, last_pos, pos - last_pos);
builder.append(replace);
last_pos = pos + search.length();
}
builder.append(s, last_pos, std::string::npos);
s = std::move(builder);
}

static std::string gguf_kv_to_str(const struct gguf_context * ctx_gguf, int i) {
Expand Down
17 changes: 11 additions & 6 deletions src/llama-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@ void llama_log_callback_default(ggml_log_level level, const char * text, void *
//

static void replace_all(std::string & s, const std::string & search, const std::string & replace) {
if (search.empty()) {
return; // Avoid infinite loop if 'search' is an empty string
}
if (search.empty())
return;
std::string builder;
builder.reserve(s.length());
size_t pos = 0;
while ((pos = s.find(search, pos)) != std::string::npos) {
s.replace(pos, search.length(), replace);
pos += replace.length();
size_t last_pos = 0;
while ((pos = s.find(search, last_pos)) != std::string::npos) {
builder.append(s, last_pos, pos - last_pos);
builder.append(replace);
last_pos = pos + search.length();
}
builder.append(s, last_pos, std::string::npos);
s = std::move(builder);
}

0 comments on commit b3c41a8

Please sign in to comment.