From b4193a44a245b16bbf90b9c79c1db97540d3c097 Mon Sep 17 00:00:00 2001 From: Michel Hermier Date: Thu, 23 Mar 2023 09:01:12 +0100 Subject: [PATCH 1/3] Benchmark string interpolation. --- test/benchmark/string_interpolation.wren | 26 +++++++++++++++++++ test/benchmark/string_interpolation_gc.wren | 28 +++++++++++++++++++++ util/benchmark.py | 4 +++ 3 files changed, 58 insertions(+) create mode 100644 test/benchmark/string_interpolation.wren create mode 100644 test/benchmark/string_interpolation_gc.wren diff --git a/test/benchmark/string_interpolation.wren b/test/benchmark/string_interpolation.wren new file mode 100644 index 000000000..3b960f218 --- /dev/null +++ b/test/benchmark/string_interpolation.wren @@ -0,0 +1,26 @@ +var start = System.clock + +var string1 = "some random string value" +var string2 = "some random string value" +for (i in 1..100000) { + "%(string1)" + "%(string1) " + " %(string1)" + " %(string1) " + "%(string2)" + "%(string2) " + " %(string2)" + " %(string2) " + + "%(string1)%(string2)" + "%(string1)%(string2) " + "%(string1) %(string2)" + "%(string1) %(string2) " + " %(string1)%(string2)" + " %(string1)%(string2) " + " %(string1) %(string2)" + " %(string1) %(string2) " +} + +System.print() // Make benchmark.py happy +System.print("elapsed: %(System.clock - start)") diff --git a/test/benchmark/string_interpolation_gc.wren b/test/benchmark/string_interpolation_gc.wren new file mode 100644 index 000000000..64eb89a1d --- /dev/null +++ b/test/benchmark/string_interpolation_gc.wren @@ -0,0 +1,28 @@ +var start = System.clock + +var string1 = "some random string value" +var string2 = "some random string value" +for (i in 1..100000) { + "%(string1)" + "%(string1) " + " %(string1)" + " %(string1) " + "%(string2)" + "%(string2) " + " %(string2)" + " %(string2) " + + "%(string1)%(string2)" + "%(string1)%(string2) " + "%(string1) %(string2)" + "%(string1) %(string2) " + " %(string1)%(string2)" + " %(string1)%(string2) " + " %(string1) %(string2)" + " %(string1) %(string2) " + + System.gc() +} + +System.print() // Make benchmark.py happy +System.print("elapsed: %(System.clock - start)") diff --git a/util/benchmark.py b/util/benchmark.py index 89537d8c8..c15c76367 100755 --- a/util/benchmark.py +++ b/util/benchmark.py @@ -94,6 +94,10 @@ def BENCHMARK(name, pattern): BENCHMARK("string_equals", r"""3000000""") +BENCHMARK("string_interpolation", "") + +BENCHMARK("string_interpolation_gc", "") + LANGUAGES = [ ("wren", [os.path.join(WREN_BIN, 'wren_test')], ".wren"), ("dart", ["fletch", "run"], ".dart"), From edc4a5eb8fe378c43f51c66d4eeb65717ab61255 Mon Sep 17 00:00:00 2001 From: Michel Hermier Date: Thu, 23 Mar 2023 08:57:11 +0100 Subject: [PATCH 2/3] Improve `String.+(_)` by checking if one of the side has a size of zero. --- src/vm/wren_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c index d0a121f8c..4c004e15a 100644 --- a/src/vm/wren_core.c +++ b/src/vm/wren_core.c @@ -1164,6 +1164,9 @@ DEF_PRIMITIVE(string_startsWith) DEF_PRIMITIVE(string_plus) { if (!validateString(vm, args[1], "Right operand")) return false; + + if (AS_STRING(args[0])->length == 0) RETURN_VAL(args[1]); + if (AS_STRING(args[1])->length == 0) RETURN_VAL(args[0]); RETURN_VAL(wrenStringFormat(vm, "@@", args[0], args[1])); } From f7999018fa5ce359a1fdca432a397c1efba84161 Mon Sep 17 00:00:00 2001 From: Michel Hermier Date: Thu, 23 Mar 2023 09:46:10 +0100 Subject: [PATCH 3/3] Optimize string interpolation in the compiler. --- src/vm/wren_compiler.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/vm/wren_compiler.c b/src/vm/wren_compiler.c index 92b16cac0..deb5aeade 100644 --- a/src/vm/wren_compiler.c +++ b/src/vm/wren_compiler.c @@ -2435,31 +2435,35 @@ static void literal(Compiler* compiler, bool canAssign) // ["a ", b + c, " d"].join() static void stringInterpolation(Compiler* compiler, bool canAssign) { - // Instantiate a new list. - loadCoreVariable(compiler, "List"); - callMethod(compiler, 0, "new()", 5); - + bool first = true; do { // The opening string part. - literal(compiler, false); - callMethod(compiler, 1, "addCore_(_)", 11); + if (AS_STRING(compiler->parser->previous.value)->length != 0) + { + literal(compiler, false); + if (!first) callMethod(compiler, 1, "+(_)", 4); + first = false; + } // The interpolated expression. ignoreNewlines(compiler); expression(compiler); - callMethod(compiler, 1, "addCore_(_)", 11); + callMethod(compiler, 0, "toString", 8); + if (!first) callMethod(compiler, 1, "+(_)", 4); + first = false; ignoreNewlines(compiler); } while (match(compiler, TOKEN_INTERPOLATION)); // The trailing string part. consume(compiler, TOKEN_STRING, "Expect end of string interpolation."); - literal(compiler, false); - callMethod(compiler, 1, "addCore_(_)", 11); - - // The list of interpolated parts. - callMethod(compiler, 0, "join()", 6); + if (AS_STRING(compiler->parser->previous.value)->length != 0) + { + literal(compiler, false); + ASSERT(!first, "Lexer error: invalid string interpolation token chain"); + callMethod(compiler, 1, "+(_)", 4); + } } static void super_(Compiler* compiler, bool canAssign)