openlang

outliers.log 的行数比 :outliers 计数多,8 个 future

我用 8 个 future 把 20000 条模拟潮汐读数折叠进同一个 atom。每条超过阈值的读数还会被追加到 outliers.log。汇总结果没问题,:n 始终是 20000,:outliers 每次运行都一样,因为种子是固定的。但日志的行数比计数器多。上次运行日志大约 250 行,计数器比这个略低。有些 id 出现了两次。用 --workers 1 时两边是一致的。

(defn- log-line [& parts]
  (spit (:log-file config)
        (str (str/join " " parts) "\n")
        :append true))

(defn fold-reading [st r]
  (let [lvl (:level r)]
    (when (outlier? r)
      (log-line "outlier" (:id r) (format "%.3f" lvl)))
    (cond-> (-> st
                (update :n inc)
                (update :sum + lvl)
                (update :sumsq + (* lvl lvl)))
      (outlier? r) (update :outliers inc))))

(defn record! [r]
  (swap! stats fold-reading r))

我原以为是多个线程 spit :append 导致重复写入,但那样的话计数器也应该是错的。我也检查过 partition-all 的分块,它们没有重叠,因为 :n 正好是 20000。每次运行前我都会删掉日志。同样的种子,下一次运行的行数又不一样。

统计值明明完全正确,日志怎么会不对?我要的是每个离群值正好一行。

从英语翻译查看原文

后续讨论 1

顺着彼此的依据读下去
  1. 固定链接

    我觉得罪魁祸首是 swap! 的重试。8 个 future 同时操作一个 atom,compare-and-set 有时会失败,函数就会在新值上重新执行一遍。只有成功的那次结果会被提交,所以计数器是准的。但 spit 每次尝试都会执行,包括被丢弃的那些。这也解释了为什么 --workers 1 时一致,以及为什么重复的数量每次运行都不同,对吧?atoms 参考文档说这个函数可能被调用多次,所以应该没有副作用。

    也许可以这样改,让 fold-reading 保持纯函数,大概像下面这样。

    (defn record! [r]
      (when (outlier? r)
        (log-line "outlier" (:id r) (format "%.3f" (:level r))))
      (swap! stats fold-reading r))

    这样每条读数只会记录一次,不过日志顺序不会和 swap 的顺序一致……

    从英语翻译查看原文

同一个问题的其他角度

  1. 已取消 context 但出错后 worker goroutine 仍然存活(总是 workers + 2)

    大家好, 我在写一个小型 Go CLI,叫 shotcheck,用来检查文件夹里每张 PNG 是否符合 App Store 对应位置要求的精确尺寸。一个 worker 池(默认 8 个)从 jobs 里读文件名,检查后把 Result 发到无缓冲的…

    回复 4最近活动 go
  2. 把过滤函数标成 async,卡顿一点没变

    ledgerscan 在 400k 条记录上做搜索。我把 run 改成了 async,在 main 里 await 它,本以为 100 ms 的心跳 Timer 会一直跳。结果先是几下 tick,然后打出 searching "rent"...,接着…

    回复 2最近活动 dart
  3. isolate 都结束了,对 ReceivePort 的 await for 却一直不结束

    大家好,我写了个小的 dart 工具,用来汇总温度 CSV,每个文件起一个 isolate,这样大文件不用互相等。我在 WSL 里这样跑 dart run bin/summarize.dart data/north.csv data/south.cs…

    回复 2最近活动 dart