Commit 006c1ce8 authored by Roman Alifanov's avatar Roman Alifanov

Unify benchmark files for accurate AWK vs Bash comparison

Made bench_heavy_awk.ct and bench_heavy_bash.ct identical except for @awk decorator. Fixed compound assignment with method calls that was causing different results.
parent 2abf5fbb
...@@ -100,6 +100,19 @@ class DispatchMixin: ...@@ -100,6 +100,19 @@ class DispatchMixin:
else: else:
self.emit(f'{target}=("${{__CT_RET[@]}}")') self.emit(f'{target}=("${{__CT_RET[@]}}")')
def _emit_assign_with_op(self, target: str, value: str, operator: str):
"""Emit assignment with compound operator support."""
if operator == "=":
self.emit_var_assign(target, value)
elif operator == "+=":
self.emit(f'{target}=$((${{target}} + {value}))'.replace('target', target))
elif operator == "-=":
self.emit(f'{target}=$((${{target}} - {value}))'.replace('target', target))
elif operator == "*=":
self.emit(f'{target}=$((${{target}} * {value}))'.replace('target', target))
elif operator == "/=":
self.emit(f'{target}=$((${{target}} / {value}))'.replace('target', target))
def generate_assignment(self, stmt: Assignment): def generate_assignment(self, stmt: Assignment):
if isinstance(stmt.target, MemberAccess): if isinstance(stmt.target, MemberAccess):
if isinstance(stmt.target.object, ThisExpr): if isinstance(stmt.target.object, ThisExpr):
...@@ -245,7 +258,7 @@ class DispatchMixin: ...@@ -245,7 +258,7 @@ class DispatchMixin:
method = callee.member method = callee.member
args_str = " ".join([f'"{a}"' for a in args]) args_str = " ".join([f'"{a}"' for a in args])
self.emit(f'__ct_class_{self.current_class}_{method} "$this" {args_str} >/dev/null') self.emit(f'__ct_class_{self.current_class}_{method} "$this" {args_str} >/dev/null')
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
if isinstance(callee.object, MemberAccess) and isinstance(callee.object.object, ThisExpr): if isinstance(callee.object, MemberAccess) and isinstance(callee.object.object, ThisExpr):
...@@ -258,7 +271,7 @@ class DispatchMixin: ...@@ -258,7 +271,7 @@ class DispatchMixin:
if method in ARR_METHODS: if method in ARR_METHODS:
arr_name = f'"${{this}}_{field_name}"' arr_name = f'"${{this}}_{field_name}"'
self.emit(f'{ARR_METHODS[method]} {arr_name} {args_str} >/dev/null'.strip()) self.emit(f'{ARR_METHODS[method]} {arr_name} {args_str} >/dev/null'.strip())
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
else: else:
self._validate_type_method("array", method, location) self._validate_type_method("array", method, location)
...@@ -266,14 +279,14 @@ class DispatchMixin: ...@@ -266,14 +279,14 @@ class DispatchMixin:
if method in DICT_METHODS: if method in DICT_METHODS:
dict_ref = f'"${{__CT_OBJ[\\"$this.{field_name}\\"]}}"' dict_ref = f'"${{__CT_OBJ[\\"$this.{field_name}\\"]}}"'
self.emit(f'{DICT_METHODS[method]} {dict_ref} {args_str} >/dev/null'.strip()) self.emit(f'{DICT_METHODS[method]} {dict_ref} {args_str} >/dev/null'.strip())
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
else: else:
self._validate_type_method("dict", method, location) self._validate_type_method("dict", method, location)
if field_type == "object": if field_type == "object":
obj_ref = f'${{__CT_OBJ["$this.{field_name}"]}}' obj_ref = f'${{__CT_OBJ["$this.{field_name}"]}}'
self.emit(f'__ct_call_method "{obj_ref}" "{method}" {args_str} >/dev/null') self.emit(f'__ct_call_method "{obj_ref}" "{method}" {args_str} >/dev/null')
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
return False return False
...@@ -293,7 +306,7 @@ class DispatchMixin: ...@@ -293,7 +306,7 @@ class DispatchMixin:
func_name = ARR_METHODS[method] func_name = ARR_METHODS[method]
if method == "push" and len(args) == 1: if method == "push" and len(args) == 1:
self.emit(f'{obj_name}+=("{args[0]}")') self.emit(f'{obj_name}+=("{args[0]}")')
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
elif method in ("map", "filter") and len(stmt.value.arguments) >= 1: elif method in ("map", "filter") and len(stmt.value.arguments) >= 1:
first_arg = stmt.value.arguments[0] first_arg = stmt.value.arguments[0]
if isinstance(first_arg, Lambda): if isinstance(first_arg, Lambda):
...@@ -305,7 +318,7 @@ class DispatchMixin: ...@@ -305,7 +318,7 @@ class DispatchMixin:
self._emit_array_assign(target) self._emit_array_assign(target)
else: else:
self.emit(f'{func_name} "{obj_name}" {args_str} >/dev/null'.replace(' ', ' ')) self.emit(f'{func_name} "{obj_name}" {args_str} >/dev/null'.replace(' ', ' '))
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
else: else:
self._validate_type_method("array", method, location) self._validate_type_method("array", method, location)
...@@ -314,7 +327,7 @@ class DispatchMixin: ...@@ -314,7 +327,7 @@ class DispatchMixin:
if method in DICT_METHODS: if method in DICT_METHODS:
func_name = DICT_METHODS[method] func_name = DICT_METHODS[method]
self.emit(f'{func_name} "{obj_name}" {args_str} >/dev/null'.replace(' ', ' ')) self.emit(f'{func_name} "{obj_name}" {args_str} >/dev/null'.replace(' ', ' '))
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
else: else:
self._validate_type_method("dict", method, location) self._validate_type_method("dict", method, location)
...@@ -323,7 +336,7 @@ class DispatchMixin: ...@@ -323,7 +336,7 @@ class DispatchMixin:
if method in FILE_HANDLE_METHODS: if method in FILE_HANDLE_METHODS:
func_name = FILE_HANDLE_METHODS[method] func_name = FILE_HANDLE_METHODS[method]
self.emit(f'{func_name} "${obj_name}" {args_str} >/dev/null'.replace(' ', ' ')) self.emit(f'{func_name} "${obj_name}" {args_str} >/dev/null'.replace(' ', ' '))
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
else: else:
self._validate_type_method("file_handle", method, location) self._validate_type_method("file_handle", method, location)
...@@ -332,12 +345,12 @@ class DispatchMixin: ...@@ -332,12 +345,12 @@ class DispatchMixin:
obj = self.generate_expr(callee.object) obj = self.generate_expr(callee.object)
func_name = STR_METHODS[method] func_name = STR_METHODS[method]
self.emit(f'{func_name} "{obj}" {args_str} >/dev/null'.replace(' ', ' ')) self.emit(f'{func_name} "{obj}" {args_str} >/dev/null'.replace(' ', ' '))
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
obj = self.generate_expr(callee.object) obj = self.generate_expr(callee.object)
self.emit(f'__ct_call_method "{obj}" "{method}" {args_str} >/dev/null') self.emit(f'__ct_call_method "{obj}" "{method}" {args_str} >/dev/null')
self.emit_var_assign(target, '$__CT_RET') self._emit_assign_with_op(target, '$__CT_RET', stmt.operator)
return True return True
return False return False
......
# Heavy string benchmark - AWK only # Heavy string benchmark - AWK only
@awk @awk
func awk_process_lines (n) { func process_lines (n) {
result = "" result = ""
for i in range (n) { for i in range (n) {
line = "item_" .. i .. "_data_" .. (i * 7) .. "_end" line = "item_" .. i .. "_data_" .. (i * 7) .. "_end"
...@@ -14,14 +14,12 @@ func awk_process_lines (n) { ...@@ -14,14 +14,12 @@ func awk_process_lines (n) {
} }
@awk @awk
func awk_word_stats (n) { func word_stats (n) {
total_len = 0 total_len = 0
word_count = 0 word_count = 0
for i in range (n) { for i in range (n) {
words = "alpha beta gamma delta epsilon zeta eta theta" words = "alpha beta gamma delta epsilon zeta eta theta"
num = words.split (" ") foreach word in words.split (" ") {
for j in range (1, num + 1) {
word = __split_arr[j]
total_len += word.len () total_len += word.len ()
word_count += 1 word_count += 1
} }
...@@ -30,7 +28,7 @@ func awk_word_stats (n) { ...@@ -30,7 +28,7 @@ func awk_word_stats (n) {
} }
@awk @awk
func awk_build_csv (rows, cols) { func build_csv (rows, cols) {
result = "" result = ""
for r in range (rows) { for r in range (rows) {
for c in range (cols) { for c in range (cols) {
...@@ -46,7 +44,7 @@ func awk_build_csv (rows, cols) { ...@@ -46,7 +44,7 @@ func awk_build_csv (rows, cols) {
} }
@awk @awk
func awk_search_pattern (n) { func search_pattern (n) {
count = 0 count = 0
for i in range (n) { for i in range (n) {
text = "error_log_" .. i .. "_warning_" .. (i % 100) .. "_info" text = "error_log_" .. i .. "_warning_" .. (i % 100) .. "_info"
...@@ -64,7 +62,7 @@ func awk_search_pattern (n) { ...@@ -64,7 +62,7 @@ func awk_search_pattern (n) {
} }
@awk @awk
func awk_concat_heavy (n) { func concat_heavy (n) {
s = "" s = ""
for i in range (n) { for i in range (n) {
s = s .. "x" s = s .. "x"
...@@ -73,9 +71,9 @@ func awk_concat_heavy (n) { ...@@ -73,9 +71,9 @@ func awk_concat_heavy (n) {
} }
print ("=== AWK String Benchmark ===") print ("=== AWK String Benchmark ===")
print ("process_lines(5000): {awk_process_lines (5000)}") print ("process_lines(5000): {process_lines (5000)}")
print ("word_stats(3000): {awk_word_stats (3000)}") print ("word_stats(3000): {word_stats (3000)}")
print ("build_csv(200,50): {awk_build_csv (200, 50)}") print ("build_csv(200,50): {build_csv (200, 50)}")
print ("search_pattern(10000): {awk_search_pattern (10000)}") print ("search_pattern(10000): {search_pattern (10000)}")
print ("concat_heavy(10000): {awk_concat_heavy (10000)}") print ("concat_heavy(10000): {concat_heavy (10000)}")
print ("Done!") print ("Done!")
# Heavy string benchmark - BASH only # Heavy string benchmark - BASH only
func bash_process_lines (n) { func process_lines (n) {
result = "" result = ""
for i in range (n) { for i in range (n) {
line = "item_" .. i .. "_data_" .. (i * 7) .. "_end" line = "item_" .. i .. "_data_" .. (i * 7) .. "_end"
...@@ -12,20 +12,20 @@ func bash_process_lines (n) { ...@@ -12,20 +12,20 @@ func bash_process_lines (n) {
return result.len () return result.len ()
} }
func bash_word_stats (n) { func word_stats (n) {
total_len = 0 total_len = 0
word_count = 0 word_count = 0
for i in range (n) { for i in range (n) {
words = "alpha beta gamma delta epsilon zeta eta theta" words = "alpha beta gamma delta epsilon zeta eta theta"
foreach word in words.split (" ") { foreach word in words.split (" ") {
total_len = total_len + word.len () total_len += word.len ()
word_count = word_count + 1 word_count += 1
} }
} }
return total_len return total_len
} }
func bash_build_csv (rows, cols) { func build_csv (rows, cols) {
result = "" result = ""
for r in range (rows) { for r in range (rows) {
for c in range (cols) { for c in range (cols) {
...@@ -40,24 +40,24 @@ func bash_build_csv (rows, cols) { ...@@ -40,24 +40,24 @@ func bash_build_csv (rows, cols) {
return result.len () return result.len ()
} }
func bash_search_pattern (n) { func search_pattern (n) {
count = 0 count = 0
for i in range (n) { for i in range (n) {
text = "error_log_" .. i .. "_warning_" .. (i % 100) .. "_info" text = "error_log_" .. i .. "_warning_" .. (i % 100) .. "_info"
if text.contains ("warning") { if text.contains ("warning") {
count = count + 1 count += 1
} }
if text.contains ("error") { if text.contains ("error") {
count = count + 1 count += 1
} }
if text.contains ("info") { if text.contains ("info") {
count = count + 1 count += 1
} }
} }
return count return count
} }
func bash_concat_heavy (n) { func concat_heavy (n) {
s = "" s = ""
for i in range (n) { for i in range (n) {
s = s .. "x" s = s .. "x"
...@@ -66,9 +66,9 @@ func bash_concat_heavy (n) { ...@@ -66,9 +66,9 @@ func bash_concat_heavy (n) {
} }
print ("=== BASH String Benchmark ===") print ("=== BASH String Benchmark ===")
print ("process_lines(5000): {bash_process_lines (5000)}") print ("process_lines(5000): {process_lines (5000)}")
print ("word_stats(3000): {bash_word_stats (3000)}") print ("word_stats(3000): {word_stats (3000)}")
print ("build_csv(200,50): {bash_build_csv (200, 50)}") print ("build_csv(200,50): {build_csv (200, 50)}")
print ("search_pattern(10000): {bash_search_pattern (10000)}") print ("search_pattern(10000): {search_pattern (10000)}")
print ("concat_heavy(10000): {bash_concat_heavy (10000)}") print ("concat_heavy(10000): {concat_heavy (10000)}")
print ("Done!") print ("Done!")
...@@ -125,6 +125,48 @@ class TestArithmetic: ...@@ -125,6 +125,48 @@ class TestArithmetic:
assert "1" in stdout assert "1" in stdout
class TestCompoundAssignment:
def test_plus_assign_simple(self):
code, stdout, _ = run_ct('''
x = 5
x += 3
print (x)
''')
assert code == 0
assert "8" in stdout
def test_plus_assign_with_method_call(self):
code, stdout, _ = run_ct('''
total = 0
word = "hello"
total += word.len ()
print (total)
''')
assert code == 0
assert "5" in stdout
def test_plus_assign_in_loop(self):
code, stdout, _ = run_ct('''
total = 0
foreach word in "ab cde f".split (" ") {
total += word.len ()
}
print (total)
''')
assert code == 0
assert "6" in stdout
def test_minus_assign_with_method(self):
code, stdout, _ = run_ct('''
x = 10
s = "abc"
x -= s.len ()
print (x)
''')
assert code == 0
assert "7" in stdout
class TestFunctions: class TestFunctions:
def test_function_call(self): def test_function_call(self):
code, stdout, _ = run_ct(''' code, stdout, _ = run_ct('''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment