twpipeline 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 79331304e3921fe1d8dcbd3b47154688dfcbea28315e5e4f90a4f8c8cde3f2be
4
+ data.tar.gz: f4d2e3acc42108b52edce6f74a98683f1fb27aeeaabee6a8adfb48a3a52fb0fd
5
+ SHA512:
6
+ metadata.gz: bc121f3c3ec181db6939aaf78b758d17c50d3882eb1150a877988cd24576596d7387716fc358b0946a2d1a57e2817d4141ccbc080f915f274de4ee4daf2232a4
7
+ data.tar.gz: e59bfc90d8989ca058e222f0d1f3bbcfb391644809bb7fadbb0bd867b438ba675098d057b43fdfa9cc98370c598596d7b97527e91891be03694a525d5ddabbe2
data/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-08-03
4
+
5
+ First release.
6
+
7
+ - Nine JSONL stages from raw crawl to n-gram tables: source gating (`01_ingest`),
8
+ MOE punctuation normalization (`02_normalize`), sentence segmentation
9
+ (`03_segment`), character-inventory and lexical-origin filtering (`04_script`,
10
+ `05_lexicon`, both delegated to twfilter 0.1.0), block-level evidence judgment
11
+ (`06_evidence`), exact type deduplication with boilerplate flagging
12
+ (`07_dedup`), Viterbi word segmentation (`08_tokenize`) and external-sort
13
+ n-gram counting with a publication floor and `--once-per host` semantics
14
+ (`09_count`)
15
+ - One record contract: stages add fields and typed findings, never remove them;
16
+ `--drop` is the only thing that discards a record
17
+ - Resource envelope: `--cores` / `--memory` with `TWP_CORES` / `TWP_MEMORY` and
18
+ autodetection; byte-bounded chunking; counting bounded by `sort -S`, not by a
19
+ hash table
20
+ - Per-stage measurement into `work/benchmarks.jsonl` via `getrusage(2)`, rendered
21
+ to BENCHMARKS.md
22
+ - HPLT source adapter with the `.tw` ∪ `html_lang` gate, hard regional TLD
23
+ exclusion and an allowlist hook (shipped empty, with 300 documented candidates)
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Den Patin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # twpipeline
2
+
3
+ Corpus construction for Taiwan Mandarin. Nine stages, one JSONL record contract,
4
+ `stdin` → `stdout`. Every stage is independently runnable and independently
5
+ verifiable. No record is ever discarded silently: rejection sets `ok: false` and
6
+ appends a typed finding; `--drop` is what removes it.
7
+
8
+ Linguistic decisions live in [twfilter](https://github.com/taiwan-corpora/twfilter); this repository is the
9
+ execution layer — sources, parallelism, external sorting, measurement.
10
+
11
+ For corpus linguists and dataset engineers who need Taiwan Mandarin
12
+ (`cmn-Hant-TW`) material with verifiable provenance. In: a web crawl or any
13
+ JSONL of documents. Out: filtered sentence types, word segmentation, n-gram and
14
+ frequency tables with per-host counting and a publication floor — the process
15
+ behind [twngrams](https://huggingface.co/datasets/taiwan-corpora/twngrams).
16
+ Single machine, no cluster: the full HPLT Taiwan slice (2.0 M documents)
17
+ builds in under two hours on a laptop.
18
+
19
+ ## Pipeline
20
+
21
+ ```mermaid
22
+ flowchart TD
23
+ subgraph acquire["acquisition"]
24
+ S1["<b>01_ingest</b> — source adapter<br/>HPLT gate: <code>.tw</code> ∪ <code>html_lang ∈ zh-TW</code> ∪ allowlist<br/>hard exclusion: <code>.cn .hk .mo .sg .my</code><br/><code>--host-cap N</code> bounds per-host concentration"]
25
+ end
26
+
27
+ subgraph shape["orthographic normalization — mandatory for publishable text"]
28
+ S2["<b>02_normalize</b> — MOE 《重訂標點符號手冊》<br/>“ ” → 「 」 · → ‧ … → …… — → ──<br/>fullwidth Latin → ASCII, invisibles stripped"]
29
+ S3["<b>03_segment</b> — sentence boundaries<br/>terminators <code>[。!?…;]+[」』)》〉]*</code>, <code>、</code> is not a boundary<br/>shape: han ∈ [6, 60], han ratio ≥ 0.65"]
30
+ end
31
+
32
+ subgraph origin["origin filter — mandatory"]
33
+ S4["<b>04_script</b> — character inventory<br/>MOE 常用 4 808 / 次常用 6 343 / 罕用 18 356 + 12 exceptions<br/>OpenCC STCharacters ∖ TSCharacters round-trip<br/>converted orthography 裏着衞爲説麽…"]
34
+ S5["<b>05_lexicon</b> — lexical origin<br/>149 hard + 38 soft mainland pairs, corpus-verified<br/>written Cantonese, Hong Kong and Singapore forms, foreign-topic tag, erhua two-window rule<br/>literary-Chinese density > 0.05 with ≥ 2 hits and ≥ 8 han"]
35
+ end
36
+
37
+ subgraph corpus["corpus hygiene — optional, source-dependent"]
38
+ S6["<b>06_evidence</b> — block judgment<br/>window 300 sentences; reject block if any member fails<br/>require ≥ 1.0 Taiwan evidence per 100 sentences"]
39
+ S7["<b>07_dedup</b> — exact type dedup<br/>SHA-256 → 62-bit fingerprint<br/>flags types occurring on ≥ 5 distinct hosts as boilerplate"]
40
+ end
41
+
42
+ subgraph measure["measurement"]
43
+ S8["<b>08_tokenize</b> — Viterbi over a unigram cost model<br/>EM re-estimation, max word length 8"]
44
+ S9["<b>09_count</b> — n-gram counting by external sort<br/><code>sort -S … --parallel …</code> ∘ <code>uniq -c</code><br/>frequency floor for publication (Google Ngram rule)"]
45
+ end
46
+
47
+ S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7 --> S8 --> S9
48
+ S9 --> OUT["frequency, dispersion (DP),<br/>collocations (G², logDice)"]
49
+
50
+ classDef must fill:#0b3d2e,stroke:#0b3d2e,color:#fff
51
+ classDef may fill:#3d3a0b,stroke:#3d3a0b,color:#fff
52
+ class S2,S3,S4,S5 must
53
+ class S6,S7 may
54
+ ```
55
+
56
+ Green stages are mandatory for any Taiwan Mandarin corpus. Amber stages depend
57
+ on the source: block judgment assumes documents are topically coherent, type
58
+ deduplication assumes the source is web-scale.
59
+
60
+ ## Record contract
61
+
62
+ One JSON object per line. Stages add fields; they never remove them.
63
+
64
+ | field | stage | meaning |
65
+ | --- | --- | --- |
66
+ | `id`, `source` | 01 | stable identity |
67
+ | `url`, `host`, `gate` | 01 | provenance and the gate that admitted it |
68
+ | `register`, `doc_score` | 01 | HPLT web-register argmax, mean segment score |
69
+ | `text` | 01–02 | document text, then punctuation-normalized |
70
+ | `doc`, `index`, `han` | 03 | parent document, ordinal, han count |
71
+ | `tier` | 04 | 0 常用, 1 次常用, 2 罕用 |
72
+ | `evidence` | 06 | Taiwan evidence count over the block |
73
+ | `tokens` | 08 | word segmentation |
74
+ | `ok`, `findings[]` | all | verdict and every reason for it |
75
+
76
+ A finding is `{check, code, severity, detail}` with `severity ∈ {reject, mark}`.
77
+
78
+ ## Install
79
+
80
+ Ruby ≥ 3.4. The only runtime dependency is
81
+ [twfilter](https://rubygems.org/gems/twfilter), pulled from RubyGems:
82
+
83
+ ```bash
84
+ bundle install
85
+ bin/twpipeline stages
86
+ ```
87
+
88
+ External sorting uses `gsort` (GNU coreutils) when installed and the system
89
+ `sort` otherwise; both understand `-S` and `--parallel`. Override with
90
+ `TWP_SORT`.
91
+
92
+ ## Usage
93
+
94
+ ```bash
95
+ twpipeline run --source hplt --input cmn_Hant/7_1.jsonl.zst --cores 16 --memory 8G
96
+ cat raw.jsonl | twpipeline 04_script | twpipeline 05_lexicon > checked.jsonl
97
+ echo '這個政策有沒有經過評估?' | twpipeline check
98
+ twpipeline run --from 03_segment --to 07_dedup --drop --policy publishable
99
+ twpipeline resources
100
+ twpipeline stages
101
+ ```
102
+
103
+ `--policy corpus` (default) keeps everything Taiwan-attested. `--policy
104
+ publishable` restricts to MOE 常用 4 808, rejects soft mainland markers, and
105
+ enforces the strict MOE punctuation inventory — the boundary of what may reach a
106
+ commercial learner product.
107
+
108
+ ## Resources
109
+
110
+ Cores and memory are read from `--cores` / `--memory`, then `TWP_CORES` /
111
+ `TWP_MEMORY`, then autodetection (all cores, half of physical memory).
112
+ `--ask` prompts interactively.
113
+
114
+ Streaming stages fork `cores` workers per chunk; chunk size is bounded by bytes
115
+ (`memory / 32`, capped at 1 GiB, overridable with `TWP_CHUNK_BYTES`), not by
116
+ line count, so a source of 12 KiB documents and a source of 40-byte sentences
117
+ both stay within the same envelope. Counting never holds a hash table: keys go
118
+ through `sort -S <memory> --parallel <cores>`, so peak resident size is set by
119
+ the flag, not by the corpus.
120
+
121
+ Every stage writes one line to `work/benchmarks.jsonl` — wall time, CPU seconds,
122
+ mean cores used, peak RSS, and the stage's own counters. The work directory
123
+ defaults to `../work` beside the checkout; override with `TWP_WORK`. See
124
+ [BENCHMARKS.md](BENCHMARKS.md).
125
+
126
+ ## Findings
127
+
128
+ Measured on HPLT 3.0 `cmn_Hant`, quality bins 7–10 (4 116 754 documents). The
129
+ admission gate kept 2 001 161 documents: 541 467 by `.tw` host, 1 459 694 by
130
+ `html_lang`. All of them were carried through stages 02–07 in 28 minutes
131
+ (twfilter 0.1.0, the published tables):
132
+
133
+ | stage | in | out | lost |
134
+ | --- | ---: | ---: | ---: |
135
+ | 03_segment | 2 001 161 docs | 37 772 352 sentences | — |
136
+ | 04_script | 37 772 352 | 37 122 017 | 1.72 % |
137
+ | 05_lexicon | 37 122 017 | 36 455 533 | 1.80 % |
138
+ | 06_evidence | 36 455 533 | 29 089 952 | 20.20 % |
139
+ | 07_dedup | 29 089 952 | **12 339 761 types** | **57.58 %** |
140
+
141
+ Three results worth stating plainly.
142
+
143
+ **Deduplication dominates every linguistic filter.** More than half of all
144
+ segmented sentences are exact repeats of another sentence — an order of magnitude
145
+ more than the three origin filters remove between them. HPLT deduplicates
146
+ documents, not sentences, and the Taiwanese long tail is largely search-optimized
147
+ loan and hotel templates. Without stage 07 an n-gram table over this material
148
+ measures template frequency, not language.
149
+
150
+ A stratified 279 198-document sample drawn from the head of each bin file put
151
+ duplication at 71.0 % and evidence loss at 5.6 %; the true figures are 57.6 % and
152
+ 20.2 %. Documents cluster by host inside a bin file, so a head sample overstates
153
+ local repetition and understates topical variety. Sample to find the shape of a
154
+ problem; measure on everything before publishing a number.
155
+
156
+ **The two gates differ, but less than a head sample suggests.** Unique
157
+ Taiwan-attested types produced per admitted document:
158
+
159
+ | gate | documents | unique types | types per document |
160
+ | --- | ---: | ---: | ---: |
161
+ | `.tw` host | 541 467 | 4 230 061 | **7.81** |
162
+ | `html_lang` on a generic TLD | 1 459 694 | 8 109 700 | **5.56** |
163
+
164
+ A 1.41× gradient, not the 2.4× the sample showed. The `html_lang` gate supplies
165
+ 65.7 % of the final corpus at about two thirds of the per-document yield — worth keeping,
166
+ not worth trusting blindly. The `gate` field is preserved on every record, so
167
+ restricting to `.tw` afterwards costs nothing and remains available as a
168
+ higher-precision subset.
169
+
170
+ **Boilerplate is sub-sentential, so no amount of sentence deduplication removes
171
+ it.** The phrase 提供相關細節的諮詢服務 occurs 263 210 times in the deduplicated
172
+ corpus, inside 263 210 *distinct* sentences, across 222 900 documents — but only
173
+ **1 948 hosts**. Sentence dedup cannot see it (every string differs), per-document
174
+ counting cannot see it (every document differs), and `--host-cap` cannot see it
175
+ (the network spans 11 691 throwaway domains, none of them large). 貸款 comes out
176
+ as the 7th most frequent word in Taiwan Mandarin, which is false.
177
+
178
+ The fix is to change what a count means. `--once-per host` counts each n-gram
179
+ once per host instead of once per occurrence, so a frequency list measures *how
180
+ many independent sites use a phrase* rather than *how many times a template was
181
+ published*. On the phrase above that is a 135-fold correction. For web-derived
182
+ frequency this is the correct semantics regardless of spam: a generated page is
183
+ not an independent act of authorship.
184
+
185
+ Occurrence counting remains the default, because for an edited corpus — statutes,
186
+ news, dictionaries — repetition is evidence rather than noise.
187
+
188
+ Measured effect on the full corpus. 貸款 falls from rank 7 to rank **1 299**;
189
+ 借錢, 包養, 二胎 and 信貸 drop below the 40-host floor and disappear entirely. The
190
+ 1-gram list becomes 的 是 有 一 在 了 我 會 也 要 不 個, the 4-gram list 越來越多,
191
+ 這是一個, 最重要的是, 更重要的是. In the run where both semantics were built,
192
+ published rows fell by 7 % at order 1 and 28 % at order 4.
193
+
194
+ **Known residual.** Eleven of the top hundred 4-grams are fragments of one
195
+ string: `讓人們盡情分享,將這個世界變得更開闊,聯繫更緊密` — the Chinese Facebook
196
+ tagline, carried by the social-plugin embed on roughly 3 900 hosts, about once
197
+ per host, so per-host counting cannot suppress it: those hosts really are
198
+ independent. This is third-party embedded interface text, a
199
+ different problem from spam, and it needs either HTML-level boilerplate stripping
200
+ upstream or an explicit stoplist. It contaminates 0.03 % of sentences but a
201
+ visible share of the 4-gram head.
202
+
203
+ The surviving corpus spans 149 022 hosts: top 10 hold 8.8 % of sentences, top 100
204
+ 20.8 %, top 1000 46.3 %, top 10 000 82.8 %. Diffuse enough to be a corpus rather
205
+ than a scrape of a few sites, but the single largest host is `chinatimes.com` at
206
+ 411 005 sentences — 3.3 % of everything, one newspaper. Concentration of one
207
+ rightsholder's expression, not total size, is what has drawn takedowns against
208
+ comparable datasets; cap per host before publishing anything that carries text.
209
+
210
+ ## Layout
211
+
212
+ ```text
213
+ lib/twpipeline/
214
+ resources.rb cores and memory resolution
215
+ parallel.rb fork-based map, byte-bounded streaming
216
+ sorting.rb external sort, tally with a frequency floor
217
+ jsonl.rb record contract
218
+ bench.rb measurement, appended to work/benchmarks.jsonl
219
+ rusage.rb getrusage(2) via Fiddle
220
+ segmenter.rb Viterbi, EM re-estimation
221
+ frequency.rb deviation of proportions
222
+ stage.rb Stage, MapStage, registry
223
+ stages/ the nine stages
224
+ sources/ source adapters
225
+ tools/
226
+ regression.rb twfilter against every reference corpus
227
+ ```
228
+
229
+ ## Tests
230
+
231
+ ```bash
232
+ bundle exec rake
233
+ ```
234
+
235
+ RSpec suite plus RBS signature validation. Fixtures carry no corpus material;
236
+ the suite needs no data and no credentials.
data/bin/twpipeline ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path("../lib", __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ begin
8
+ require "twpipeline"
9
+ rescue LoadError
10
+ $LOAD_PATH.unshift(File.expand_path("../../twfilter/lib", __dir__))
11
+ require "twpipeline"
12
+ end
13
+ require "twpipeline/cli"
14
+
15
+ exit(TWPipeline::CLI.call(ARGV))
@@ -0,0 +1,26 @@
1
+ # HPLT gate data
2
+
3
+ `taiwan_domains.txt` — the curated allowlist read by `01_ingest --allowlist`.
4
+ **Empty by default, and that is deliberate.**
5
+
6
+ `taiwan_domains.candidates.txt` — 300 hosts derived automatically from the
7
+ 1 927 814 documents that failed both the `.tw` and the `html_lang` gates in HPLT
8
+ 3.0 `cmn_Hant` bins 7–10. A host qualified on: ≥ 5 documents sampled, zero
9
+ origin-filter rejections, and ≥ 1.0 Taiwan evidence per 100 han characters.
10
+
11
+ The list is published as *candidates*, not as an allowlist, because its
12
+ precision is poor for the intended purpose. Ranked by evidence density, the top
13
+ of the list is `downloadcenter.intel.com`, `powerapps.microsoft.com`,
14
+ `developer.microsoft.com`, `dvdvideosoft.com`, `veed.io` — localized zh-TW
15
+ product pages. They pass every origin test because localization vendors use
16
+ correct Taiwanese IT vocabulary, but they are `production: translated`, not
17
+ native Taiwan Mandarin. Admitting them would reintroduce exactly the
18
+ translationese the provenance axes exist to exclude.
19
+
20
+ The 300 hosts would have added 2 812 of 859 049 sampled documents — 0.3 % more
21
+ volume for a measurable loss of precision. Curate by hand from the candidates
22
+ file, or leave the allowlist empty.
23
+
24
+ `tools/hplt_allowlist.rb` writes the full evidence table for every host
25
+ examined — documents, han characters, evidence per 100 han, rejection rate — to
26
+ `work/hplt/allowlist_report.tsv`.
@@ -0,0 +1,300 @@
1
+ 173ng.com
2
+ 1989wolfe.com
3
+ 3dayseo.com
4
+ 5xruby.com
5
+ 9jgirl.com
6
+ accessories.ap.dell.com
7
+ adai69.blogspot.com
8
+ adobe.com
9
+ advertise.bingads.microsoft.com
10
+ aiot.innodisk.com
11
+ albertinformation.blogspot.com
12
+ amedeephotography.com
13
+ ancad.com
14
+ android.lisisoft.com
15
+ angela700422.pixnet.net
16
+ animebase.org
17
+ applewoods.org
18
+ aquia.tian.yam.com
19
+ arielku.tian.yam.com
20
+ artchen.dev
21
+ arubanetworks.com
22
+ asusfoundation.org
23
+ avast.com
24
+ bakesmith.taipei
25
+ barbachacha.com
26
+ baristatotw.blogspot.ca
27
+ beautyfashion-tw.com
28
+ beckysfashionlifestyle.com
29
+ best007.org
30
+ blog.adahsu.net
31
+ blog.alexw.net
32
+ blog.asuscloud.com
33
+ blog.balduran.cc
34
+ blog.cashwu.com
35
+ blog.changyy.org
36
+ blog.gslin.org
37
+ blog.jln.co
38
+ blog.jw910731.me
39
+ blog.miniasp.com
40
+ blog.monkeypotion.net
41
+ blog.poychang.net
42
+ blog.roroiii.com
43
+ blog.schokomi.com
44
+ blog.steveyi.net
45
+ blog.tedshd.io
46
+ blog.ueplay.com
47
+ blog.wabow.com
48
+ blog.wing0826.com
49
+ blog.yilang.org
50
+ bonwawa.blogspot.com
51
+ boringfreeware.blogspot.com
52
+ bouken.space
53
+ bt.cream123.com
54
+ buy.hpe.com
55
+ cadch.com
56
+ car.u-3c.com
57
+ casestudies.engenius.ai
58
+ cchsu.com
59
+ ccskin.com
60
+ changeanddiffusion.blogspot.com
61
+ cherstravel.com
62
+ chia0302.tian.yam.com
63
+ chinese.hu-hiu.xyz
64
+ chnlnkuo.blogspot.com
65
+ cloud-say.com
66
+ cognex.com
67
+ community.oppo.com
68
+ cool007.org
69
+ cyberformula.tian.yam.com
70
+ ddselectivefashion.com
71
+ delltechnologies.com
72
+ desa713.tian.yam.com
73
+ developer.microsoft.com
74
+ developers.facebook.com
75
+ diazue65.tian.yam.com
76
+ digital.ni.com
77
+ dinewithkirsty.com
78
+ divakalife.blogspot.com
79
+ docs.adobe.com
80
+ docs.blackberry.com
81
+ docs.plesk.com
82
+ docs.vmware.com
83
+ dokuroko.blog128.fc2.com
84
+ donation-networks.savedogs.org
85
+ downloadcenter.intel.com
86
+ dualsnake.tian.yam.com
87
+ duomaxwellr.blogspot.com
88
+ dvdvideosoft.com
89
+ easy.twmoneyloan.com
90
+ easyloan.taipei
91
+ enspyre.com
92
+ ericsk.org
93
+ estinet.com
94
+ event.asus.com
95
+ experience.dropbox.com
96
+ f4mglendo.tian.yam.com
97
+ fenixhsu.medium.com
98
+ fitnfun.biz
99
+ fortinet.com
100
+ forum.macuknow.com
101
+ fred-zone.blogspot.com
102
+ freedom.support.tm
103
+ fullstackladder.dev
104
+ furnacedigital.blogspot.com
105
+ gemstw.com
106
+ girlstalk.cc
107
+ go-traveler.com
108
+ go-tribe-ycc.blogspot.com
109
+ gomrcuriosity.com
110
+ goodmommom.net
111
+ grayliao.blogspot.com
112
+ harry811016.medium.com
113
+ help.mega.io
114
+ help.ptc.com
115
+ help.tableau.com
116
+ helpx.adobe.com
117
+ hitcon.org
118
+ hlisfab2015.blogspot.com
119
+ home.allproducts.com
120
+ hotzsoft.com
121
+ house-expert.com
122
+ how-to-make-money12.blogspot.com
123
+ huidu.io
124
+ hungryowl.typepad.com
125
+ hyperrate.com
126
+ ifans.pixnet.net
127
+ iiot.apacer.com
128
+ imsean.pixnet.net
129
+ indianic.com
130
+ infinitiessoft.com
131
+ ipoe.cc
132
+ jaceju.net
133
+ jerolin.tian.yam.com
134
+ jingshouse.blogspot.com
135
+ jjdtw.com
136
+ julianshen.blogspot.com
137
+ june-yu.com
138
+ juniper.net
139
+ kaiching.org
140
+ key.chtouch.com
141
+ khm-film.blogspot.com
142
+ kirkchen.logdown.com
143
+ kskb.eu.org
144
+ ktrees.com
145
+ labviewpro.net
146
+ land.gov.taipei
147
+ larrynote.com
148
+ larrynung.github.io
149
+ lbrwed.com
150
+ leasonellis.com
151
+ leonvhi.tian.yam.com
152
+ leoyeh-me.gitbook.io
153
+ letsgogonz.blogspot.co.nz
154
+ librarythings.info
155
+ lifewth.com
156
+ linda.sopili.net
157
+ line8.me
158
+ linux.vbird.org
159
+ logitechclub.com
160
+ lookerideas.net
161
+ luannukm1989.tian.yam.com
162
+ m.california-companies.com
163
+ magiclen.org
164
+ mahjong.caca888.net
165
+ mantoh247.tian.yam.com
166
+ marleen741115.tian.yam.com
167
+ mcafee.com
168
+ meritlilin.com
169
+ microsoftstore.com
170
+ midnightcoder.blogspot.com
171
+ mit-machining.com
172
+ mlytics.com
173
+ mobileapps.frederickwebb.com
174
+ moeimo2016.blogspot.com
175
+ moonpoet.com
176
+ morrisa41.tian.yam.com
177
+ movavi.com
178
+ mrs-mo.com
179
+ msdn.microsoft.com
180
+ museumms.blogspot.com
181
+ musewp.pro
182
+ mysabeauty.com
183
+ nctusdk.blogspot.com
184
+ netapp.com
185
+ nineblog.tian.yam.com
186
+ normansem.com
187
+ note.charlestw.com
188
+ novell.com
189
+ nubaby.cc
190
+ origin2.logitech.com
191
+ ouch1978.github.io
192
+ oursong.com
193
+ panypanla.blogspot.com
194
+ pccentertwschoolkf.blogspot.com
195
+ pdfshouce.com
196
+ peachwaneversay.blogspot.com
197
+ peterdavehello.org
198
+ petnii.com
199
+ pinhladysz.tian.yam.com
200
+ pipedrive.com
201
+ powerapps.microsoft.com
202
+ protonmail.com
203
+ pt2club.blogspot.com
204
+ pvencs.blogspot.com
205
+ radioecouteur.com
206
+ rafaelu8mr.tian.yam.com
207
+ rayaegis.com
208
+ redhat.com
209
+ richielin-mac.blogspot.com
210
+ roroiii.coderbridge.io
211
+ rpg2008.blogspot.com
212
+ rtaiwanr.wordpress.com
213
+ ruth7112.tian.yam.com
214
+ salesforce.com
215
+ sandrabb.pixnet.net
216
+ saverinthebox.com
217
+ sctaeaa.org
218
+ sflowerpp.tian.yam.com
219
+ shamitsu.com
220
+ sharkjiang.com
221
+ shingfay.tian.yam.com
222
+ shopaholicwifes.blogspot.it
223
+ shopifytaiwan.com
224
+ shuanet.com
225
+ shutterstock.com
226
+ si402.tian.yam.com
227
+ skype.com
228
+ slsp.auo.com
229
+ sonniekoenig.com
230
+ splunk.com
231
+ ssemble.com
232
+ ssuns.tian.yam.com
233
+ sszxfcxt.tian.yam.com
234
+ stardouble.tian.yam.com
235
+ stellahyc.pixnet.net
236
+ strangerckbe.tian.yam.com
237
+ strollmarket.com
238
+ study-area.org
239
+ support.norton.com
240
+ support.ubisoft.com
241
+ support.xbox.com
242
+ synology.com
243
+ synology.de
244
+ tacolin.blogspot.com
245
+ taiwan-photo-base.com
246
+ taiwan.arcsight.com
247
+ taiwan10000.com
248
+ taiwanese-association-of-norway.org
249
+ taiwanmooc.org
250
+ taiwannextgenfoundation.org
251
+ taiwantravelmap.com
252
+ telco.vmware.com
253
+ tgtf4s6bhrc.tian.yam.com
254
+ theonevfx.com
255
+ tos.ea.com
256
+ tpweddinggift.com
257
+ tsi.center
258
+ tso-liang-wu.gitbook.io
259
+ tukuyomimaid.blog126.fc2.com
260
+ tw.andys.pro
261
+ tw.aoc.com
262
+ tw.cansonic.com
263
+ tw.flux3dp.com
264
+ tw.ipswitch.com
265
+ tw.micron.com
266
+ tw.websense.com
267
+ twdn.net
268
+ twosevenths.com
269
+ twtybbs.com
270
+ vaderisme.tian.yam.com
271
+ vaemon.com
272
+ veed.io
273
+ vhbwxrlzghudo.tian.yam.com
274
+ vielgluck.tian.yam.com
275
+ visualstudio.com
276
+ vmware.com
277
+ w0.wetouch.me
278
+ weatherrisk.com
279
+ web.taiwanhot.net
280
+ websecurity.symantec.com
281
+ weebly.com
282
+ wemoscooter.com
283
+ windwithme.blogspot.com
284
+ wooddiylonlon.blogspot.com
285
+ wpbox.tips
286
+ wznote.blogspot.com
287
+ ya-boginhall-40.8rmi2.upcloudobjects.com
288
+ ya-boginhall-5.s3.nl-ams.scw.cloud
289
+ ya-boginhall-6.s3.pl-waw.scw.cloud
290
+ ya-catering-14.sfo3.digitaloceanspaces.com
291
+ yahootwtrip.tumblr.com
292
+ yingzaiandtei.blogspot.com
293
+ yiying.handysociality.com
294
+ yllan.org
295
+ youthfulcf.tian.yam.com
296
+ yuchengcomuseum.benchurl.com
297
+ yuhinas.blogspot.com
298
+ yurenju.medium.com
299
+ zh-tw.broadcom.com
300
+ zh.unifab.ai
File without changes
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "pathname"
5
+ require "time"
6
+
7
+ module TWPipeline
8
+ module Bench
9
+ Sample = Data.define(:label, :seconds, :cpu_seconds, :peak_rss_bytes, :facts) do
10
+ def cores_used = seconds.zero? ? 0.0 : cpu_seconds / seconds
11
+
12
+ def to_h
13
+ {
14
+ label: label,
15
+ seconds: seconds.round(3),
16
+ cpu_seconds: cpu_seconds.round(3),
17
+ cores_used: cores_used.round(2),
18
+ peak_rss_bytes: peak_rss_bytes
19
+ }.merge(facts)
20
+ end
21
+ end
22
+
23
+ module_function
24
+
25
+ def path = Pathname(ENV.fetch("TWP_BENCHMARKS", TWPipeline.work.join("benchmarks.jsonl").to_s))
26
+
27
+ def measure(label, **facts)
28
+ started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
29
+ cpu_before = cpu_seconds
30
+ result = yield
31
+ sample = Sample.new(
32
+ label: label,
33
+ seconds: Process.clock_gettime(Process::CLOCK_MONOTONIC) - started,
34
+ cpu_seconds: cpu_seconds - cpu_before,
35
+ peak_rss_bytes: peak_rss,
36
+ facts: facts.merge(result.is_a?(Hash) ? result : {})
37
+ )
38
+ record(sample)
39
+ sample
40
+ end
41
+
42
+ def cpu_seconds
43
+ times = Process.times
44
+ times.utime + times.stime + times.cutime + times.cstime
45
+ end
46
+
47
+ def peak_rss = Rusage.peak
48
+
49
+ def record(sample)
50
+ path.dirname.mkpath
51
+ path.open("a") { |io| io.puts(JSON.generate({at: Time.now.utc.iso8601}.merge(sample.to_h))) }
52
+ sample
53
+ end
54
+ end
55
+ end