From 24aff3526485b763f676f54459f28c75d80566bf Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 16:58:07 +0800 Subject: [PATCH 1/4] wip --- bench/either.scm | 92 ++++++++++++++--- devel/211_1.md | 31 ++++++ goldfish/liii/timeit.scm | 46 +++++++++ tests/goldfish/liii/timeit-test.scm | 155 ++++++++++++++++++++++++++++ 4 files changed, 309 insertions(+), 15 deletions(-) create mode 100644 devel/211_1.md create mode 100644 goldfish/liii/timeit.scm create mode 100644 tests/goldfish/liii/timeit-test.scm diff --git a/bench/either.scm b/bench/either.scm index fb5eab12..c62d1b2d 100644 --- a/bench/either.scm +++ b/bench/either.scm @@ -14,23 +14,85 @@ ; under the License. ; -(import (scheme time) - (liii lang)) +(import (liii timeit) + (liii either) + (liii lang) + (liii base)) -(define (timing msg thunk) - (let* ((start (current-jiffy)) - (val (thunk)) - (end (current-jiffy))) - (display* msg (number->string (- end start)) "\n"))) +(define (benchmark-either-operations) + (display "=== Either Operations Benchmark ===\n\n") -(define (repeat n proc) - (when (>= n 0) - (proc) - (repeat (- n 1) proc))) + ; Test either%get-or-else with right value + (let ((time (timeit (lambda () ((right 65536) :get-or-else 0))))) + (display* "either%get-or-else (right):\t" (number->string time) " seconds\n")) -(timing "either%get-or-else:\t" - (lambda () (repeat 10000 (lambda () ((right 65536) :get-or-else 0))))) + ; Test either%get-or-else with left value + (let ((time (timeit (lambda () ((left "error") :get-or-else 0))))) + (display* "either%get-or-else (left):\t" (number->string time) " seconds\n")) -(timing "either%or-else:\t" - (lambda () (repeat 10000 (lambda () ((left "error") :or-else (right 0)))))) + ; Test either%or-else with left value + (let ((time (timeit (lambda () ((left "error") :or-else (right 0))))) + (display* "either%or-else (left):\t\t" (number->string time) " seconds\n"))) + + ; Test either%or-else with right value + (let ((time (timeit (lambda () ((right 65536) :or-else (left 0))))) + (display* "either%or-else (right):\t\t" (number->string time) " seconds\n"))) + + ; Test either%left? and either%right? + (let ((time (timeit (lambda () ((right 42) :left?))))) + (display* "either%left? (right):\t\t" (number->string time) " seconds\n")) + + (let ((time (timeit (lambda () ((left "error") :right?))))) + (display* "either%right? (left):\t\t" (number->string time) " seconds\n")) + + ; Test either%get + (let ((time (timeit (lambda () ((right 42) :get))))) + (display* "either%get (right):\t\t" (number->string time) " seconds\n")) + + ; Test either%filter-or-else + (let ((time (timeit (lambda () ((right 42) :filter-or-else even? 0))))) + (display* "either%filter-or-else (right):\t" (number->string time) " seconds\n")) + + (let ((time (timeit (lambda () ((left "error") :filter-or-else even? 0))))) + (display* "either%filter-or-else (left):\t" (number->string time) " seconds\n")) + + ; Test either%contains + (let ((time (timeit (lambda () ((right 42) :contains 42))))) + (display* "either%contains (right):\t\t" (number->string time) " seconds\n")) + + (let ((time (timeit (lambda () ((left "error") :contains 42))))) + (display* "either%contains (left):\t\t" (number->string time) " seconds\n")) + + ; Test either%map + (let ((time (timeit (lambda () ((right 42) :map (lambda (x) (* x 2))))))) + (display* "either%map (right):\t\t" (number->string time) " seconds\n")) + + (let ((time (timeit (lambda () ((left "error") :map (lambda (x) (* x 2))))))) + (display* "either%map (left):\t\t" (number->string time) " seconds\n")) + + ; Test either%flat-map + (let ((time (timeit (lambda () ((right 42) :flat-map (lambda (x) (right (* x 2))))))) + (display* "either%flat-map (right):\t" (number->string time) " seconds\n"))) + + (let ((time (timeit (lambda () ((left "error") :flat-map (lambda (x) (right (* x 2))))))) + (display* "either%flat-map (left):\t" (number->string time) " seconds\n"))) + + ; Test either%to-option + (let ((time (timeit (lambda () ((right 42) :to-option))))) + (display* "either%to-option (right):\t" (number->string time) " seconds\n")) + + (let ((time (timeit (lambda () ((left "error") :to-option))))) + (display* "either%to-option (left):\t" (number->string time) " seconds\n")) + + ; Test either%forall and either%exists + (let ((time (timeit (lambda () ((right 42) :forall even?))))) + (display* "either%forall (right):\t\t" (number->string time) " seconds\n")) + + (let ((time (timeit (lambda () ((right 42) :exists even?))))) + (display* "either%exists (right):\t\t" (number->string time) " seconds\n")) + + (display "\n")) + +; Run the benchmark +(benchmark-either-operations) diff --git a/devel/211_1.md b/devel/211_1.md new file mode 100644 index 00000000..76dba4c9 --- /dev/null +++ b/devel/211_1.md @@ -0,0 +1,31 @@ +# [211_1] (liii timeit) 模块实现 + +## 任务相关的代码文件 +- goldfish/liii/timeit.scm +- tests/goldfish/liii/timeit-test.scm + +## 如何测试 +``` +xmake config --yes +xmake b goldfish +bin/goldfish tools/lint.scm goldfish/liii/timeit.scm tests/goldfish/liii/timeit-test.scm +bin/goldfish tests/goldfish/liii/timeit-test.scm +``` + +## 2025/11/02 (liii timeit) 模块实现 +### What +实现 (liii timeit) 模块,包含 `timeit` 函数用于测量代码执行时间。 + +1. 创建 `goldfish/liii/timeit.scm` 实现文件 +2. 创建 `tests/goldfish/liii/timeit-test.scm` 测试文件 +3. 实现 `timeit` 函数,接受三个具名参数:`stmt`、`setup`、`number` +4. `stmt` 参数接受无参数的 lambda 表达式,执行 `number` 次 +5. `setup` 参数接受无参数的 lambda 表达式,用于初始化,只执行一次 +6. 返回执行时间的浮点数(秒) +7. 编写全面的单元测试用例 + +### Why +提供标准化的代码执行时间测量工具,帮助开发者进行性能分析和优化,是开发过程中常用的调试和优化工具。 + +### How +使用 `define*` 语法实现具名参数,通过系统时间函数计算执行时间,确保测量精度和准确性。采用测试驱动开发方法,先编写测试用例再实现功能。 \ No newline at end of file diff --git a/goldfish/liii/timeit.scm b/goldfish/liii/timeit.scm new file mode 100644 index 00000000..045a4443 --- /dev/null +++ b/goldfish/liii/timeit.scm @@ -0,0 +1,46 @@ +; +; Copyright (C) 2024 The Goldfish Scheme Authors +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +; License for the specific language governing permissions and limitations +; under the License. +; + +(define-library (liii timeit) + (export timeit) + (import (liii base) + (scheme time)) + (begin + + (define* (timeit stmt (setup '()) (number 1000000)) + (if (not (procedure? stmt)) + (error 'type-error "(timeit stmt setup number): stmt must be a procedure")) + (if (not (or (procedure? setup) (null? setup))) + (error 'type-error "(timeit stmt setup number): setup must be a procedure or '()")) + (if (not (and (integer? number) (positive? number))) + (error 'type-error "(timeit stmt setup number): number must be a positive integer")) + + ; Execute setup once if it's not '() + (unless (null? setup) + (setup)) + + ; Get start time + (let ((start-time (current-second))) + ; Execute stmt number times + (do ((i 0 (+ i 1))) + ((= i number)) + (stmt)) + + ; Calculate elapsed time + (- (current-second) start-time))) + + ) ; end of begin + ) ; end of define-library \ No newline at end of file diff --git a/tests/goldfish/liii/timeit-test.scm b/tests/goldfish/liii/timeit-test.scm new file mode 100644 index 00000000..730a36e3 --- /dev/null +++ b/tests/goldfish/liii/timeit-test.scm @@ -0,0 +1,155 @@ +; +; Copyright (C) 2024 The Goldfish Scheme Authors +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +; License for the specific language governing permissions and limitations +; under the License. +; + +(import (liii check) + (liii timeit) + (liii time) + (liii base)) + +(check-set-mode! 'report-failed) + + +#| +timeit +测量代码执行时间。 + +语法 +---- +(timeit stmt setup number) + +参数 +---- +stmt : (lambda () any) +要测量执行时间的语句,接受一个无参数的 lambda 表达式。 + +setup : (lambda () any) +初始化代码,接受一个无参数的 lambda 表达式,只执行一次。 + +number : integer? +stmt 执行的次数,必须是正整数。 + +返回值 +----- +number? +返回执行 stmt number 次的总时间,以秒为单位的浮点数。 + +说明 +---- +1. 首先执行一次 setup 函数进行初始化 +2. 然后执行 stmt 函数 number 次 +3. 返回总执行时间(秒) +4. 主要用于性能测试和代码优化 +5. 时间测量精度取决于系统实现 + +错误处理 +-------- +type-error +当参数类型不正确时抛出错误。 + +示例 +---- +; 测量加法运算的执行时间 +(timeit (lambda () (+ 1 2)) + (lambda () #t) + 1000000) + +; 测量列表操作的时间 +(let ((lst '())) + (timeit (lambda () (set! lst (cons 1 lst))) + (lambda () (set! lst '())) + 10000)) + +|# + +; Test basic timeit functionality +(let ((result (timeit (lambda () (+ 1 2)) + (lambda () #t) + 1000))) + (check (number? result) => #t) + (check (>= result 0) => #t)) + +; Test timeit with setup function +(let ((counter 0)) + (timeit (lambda () (set! counter (+ counter 1))) + (lambda () (set! counter 0)) + 100) + (check (= counter 100) => #t)) + +; Test timeit with different number of iterations +(let ((result1 (timeit (lambda () (* 2 3)) + (lambda () #t) + 100)) + (result2 (timeit (lambda () (* 2 3)) + (lambda () #t) + 1000))) + (check (number? result1) => #t) + (check (number? result2) => #t) + (check (>= result2 result1) => #t)) + +; Test timeit with empty setup +(let ((result (timeit (lambda () (display "")) + (lambda () #t) + 10))) + (check (number? result) => #t) + (check (>= result 0) => #t)) + +; Test timeit with complex setup +(let ((lst '())) + (timeit (lambda () (set! lst (cons 'x lst))) + (lambda () (set! lst (make-list 100 'a))) + 50) + (check (= (length lst) 150) => #t)) + +; Test error handling - invalid number parameter +(check-catch 'type-error (timeit (lambda () #t) + (lambda () #t) + 'invalid)) + +; Test error handling - invalid stmt parameter +(check-catch 'type-error (timeit 'not-a-lambda + (lambda () #t) + 100)) + +; Test error handling - invalid setup parameter +(check-catch 'type-error (timeit (lambda () #t) + 'not-a-lambda + 100)) + +; Test timeit with sleep to verify timing accuracy +(let ((result (timeit (lambda () (sleep 0.1)) + (lambda () #t) + 1))) + (check (number? result) => #t) + (check (>= result 0.09) => #t) ; Should be at least 0.09 seconds + (check (<= result 0.2) => #t)) ; Should be less than 0.2 seconds + +; Test timeit with multiple sleep iterations +(let ((result (timeit (lambda () (sleep 0.01)) + (lambda () #t) + 5))) + (check (number? result) => #t) + (check (>= result 0.04) => #t) ; Should be at least 0.04 seconds (5 * 0.01) + (check (<= result 0.1) => #t)) ; Should be less than 0.1 seconds + +; Test timeit with very short sleep +(let ((result (timeit (lambda () (sleep 0.001)) + (lambda () #t) + 10))) + (check (number? result) => #t) + (check (>= result 0.005) => #t) ; Should be at least 0.005 seconds (10 * 0.001) + (check (<= result 0.05) => #t)) ; Should be less than 0.05 seconds + +(check-report) \ No newline at end of file -- Gitee From 868eba0e861122a17be3bd804251a8878796428a Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 19:28:41 +0800 Subject: [PATCH 2/4] wip --- bench/either.scm | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bench/either.scm b/bench/either.scm index c62d1b2d..1e32778b 100644 --- a/bench/either.scm +++ b/bench/either.scm @@ -31,12 +31,13 @@ (display* "either%get-or-else (left):\t" (number->string time) " seconds\n")) ; Test either%or-else with left value - (let ((time (timeit (lambda () ((left "error") :or-else (right 0))))) - (display* "either%or-else (left):\t\t" (number->string time) " seconds\n"))) + (let ((time (timeit (lambda () + ((left "error") :or-else (right 0)))))) + (display* "either%or-else (left):\t\t" (number->string time) " seconds\n")) ; Test either%or-else with right value - (let ((time (timeit (lambda () ((right 65536) :or-else (left 0))))) - (display* "either%or-else (right):\t\t" (number->string time) " seconds\n"))) + (let ((time (timeit (lambda () ((right 65536) :or-else (left 0)))))) + (display* "either%or-else (right):\t\t" (number->string time) " seconds\n")) ; Test either%left? and either%right? (let ((time (timeit (lambda () ((right 42) :left?))))) @@ -71,11 +72,11 @@ (display* "either%map (left):\t\t" (number->string time) " seconds\n")) ; Test either%flat-map - (let ((time (timeit (lambda () ((right 42) :flat-map (lambda (x) (right (* x 2))))))) - (display* "either%flat-map (right):\t" (number->string time) " seconds\n"))) + (let ((time (timeit (lambda () ((right 42) :flat-map (lambda (x) (right (* x 2)))))))) + (display* "either%flat-map (right):\t" (number->string time) " seconds\n")) - (let ((time (timeit (lambda () ((left "error") :flat-map (lambda (x) (right (* x 2))))))) - (display* "either%flat-map (left):\t" (number->string time) " seconds\n"))) + (let ((time (timeit (lambda () ((left "error") :flat-map (lambda (x) (right (* x 2)))))))) + (display* "either%flat-map (left):\t" (number->string time) " seconds\n")) ; Test either%to-option (let ((time (timeit (lambda () ((right 42) :to-option))))) -- Gitee From 714776914fc2ad8e43d1c04e6891748778f9552c Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 19:30:18 +0800 Subject: [PATCH 3/4] wip --- CLAUDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 02515145..713774d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -71,6 +71,8 @@ | 207 | (liii sort) | | 208 | (liii unicode) | | 209 | (liii oop) | +| 210 | S7内置的函数 | +| 211 | (liii timeit) | 固定的任务编号: 1. `200_0`: 更新Goldfish Scheme的AUTHORS文件 -- Gitee From b48c49d3cae92c1f06785ecf6ddafa6658c65ecd Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 19:32:35 +0800 Subject: [PATCH 4/4] wip --- goldfish/liii/timeit.scm | 2 +- tests/goldfish/liii/timeit-test.scm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/goldfish/liii/timeit.scm b/goldfish/liii/timeit.scm index 045a4443..9aee7443 100644 --- a/goldfish/liii/timeit.scm +++ b/goldfish/liii/timeit.scm @@ -1,5 +1,5 @@ ; -; Copyright (C) 2024 The Goldfish Scheme Authors +; Copyright (C) 2025 The Goldfish Scheme Authors ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. diff --git a/tests/goldfish/liii/timeit-test.scm b/tests/goldfish/liii/timeit-test.scm index 730a36e3..d68517ef 100644 --- a/tests/goldfish/liii/timeit-test.scm +++ b/tests/goldfish/liii/timeit-test.scm @@ -1,5 +1,5 @@ ; -; Copyright (C) 2024 The Goldfish Scheme Authors +; Copyright (C) 2025 The Goldfish Scheme Authors ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. -- Gitee