mirror of
				https://github.com/ggml-org/llama.cpp.git
				synced 2025-10-30 08:42:00 +00:00 
			
		
		
		
	llama : fix time complexity of string replacement (#9163)
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
This commit is contained in:
		| @@ -1861,13 +1861,19 @@ std::string string_get_sortable_timestamp() { | |||||||
|  |  | ||||||
| void string_replace_all(std::string & s, const std::string & search, const std::string & replace) { | void string_replace_all(std::string & s, const std::string & search, const std::string & replace) { | ||||||
|     if (search.empty()) { |     if (search.empty()) { | ||||||
|         return; // Avoid infinite loop if 'search' is an empty string |         return; | ||||||
|     } |     } | ||||||
|  |     std::string builder; | ||||||
|  |     builder.reserve(s.length()); | ||||||
|     size_t pos = 0; |     size_t pos = 0; | ||||||
|     while ((pos = s.find(search, pos)) != std::string::npos) { |     size_t last_pos = 0; | ||||||
|         s.replace(pos, search.length(), replace); |     while ((pos = s.find(search, last_pos)) != std::string::npos) { | ||||||
|         pos += replace.length(); |         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) { | void string_process_escapes(std::string & input) { | ||||||
|   | |||||||
| @@ -216,13 +216,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) { | static void replace_all(std::string & s, const std::string & search, const std::string & replace) { | ||||||
|     if (search.empty()) { |     if (search.empty()) { | ||||||
|         return; // Avoid infinite loop if 'search' is an empty string |         return; | ||||||
|     } |     } | ||||||
|  |     std::string builder; | ||||||
|  |     builder.reserve(s.length()); | ||||||
|     size_t pos = 0; |     size_t pos = 0; | ||||||
|     while ((pos = s.find(search, pos)) != std::string::npos) { |     size_t last_pos = 0; | ||||||
|         s.replace(pos, search.length(), replace); |     while ((pos = s.find(search, last_pos)) != std::string::npos) { | ||||||
|         pos += replace.length(); |         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) { | static std::string gguf_kv_to_str(const struct gguf_context * ctx_gguf, int i) { | ||||||
|   | |||||||
| @@ -31,11 +31,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) { | static void replace_all(std::string & s, const std::string & search, const std::string & replace) { | ||||||
|     if (search.empty()) { |     if (search.empty()) { | ||||||
|         return; // Avoid infinite loop if 'search' is an empty string |         return; | ||||||
|     } |     } | ||||||
|  |     std::string builder; | ||||||
|  |     builder.reserve(s.length()); | ||||||
|     size_t pos = 0; |     size_t pos = 0; | ||||||
|     while ((pos = s.find(search, pos)) != std::string::npos) { |     size_t last_pos = 0; | ||||||
|         s.replace(pos, search.length(), replace); |     while ((pos = s.find(search, last_pos)) != std::string::npos) { | ||||||
|         pos += replace.length(); |         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); | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Justine Tunney
					Justine Tunney