diff --git a/.github/release-notes.md b/.github/release-notes.md index 0ace5a9..38a2a86 100644 --- a/.github/release-notes.md +++ b/.github/release-notes.md @@ -8,7 +8,7 @@ | macOS | `NZBreX_@TAG@_darwin-arm64_purego.tar.gz` (Intel: `darwin-amd64`) | | FreeBSD | `NZBreX_@TAG@_freebsd-amd64_purego.tar.gz` | -**purego** is the default and fine for everyone. Its yEnc decoder (go-yenc-simd) is as fast as rapidyenc's C code on x86-64 CPUs with AVX2, and faster with AVX-512. On ARM (Apple Silicon, arm64 Linux) it has no SIMD yet, so there the **rapidyenc** files, which decode with rapidyenc's C code for `-yenctest=4`, are faster. Decoding is far faster than any download either way; `NZBreX -benchmark` shows the numbers for your machine. The rapidyenc files exist for Linux amd64/arm64, Windows amd64 and macOS arm64; the Linux ones need glibc 2.35 or newer. +**purego** is the default and fine for everyone. Its yEnc decoder (go-yenc-simd) is as fast as rapidyenc's C code on x86-64 CPUs with AVX2, and faster with AVX-512. On ARM (Apple Silicon, arm64 Linux) it uses NEON: on an Apple M1 or a Neoverse N2 the decode kernel does about 5–8 GB/s on random data, a whole article with size and CRC checks 2–3.4 GB/s (plain Go: 1.9–2.7 GB/s). The **rapidyenc** files decode with rapidyenc's C code for `-yenctest=4` instead. Decoding is far faster than any download either way; `NZBreX -benchmark` shows the numbers for your machine (the rapidyenc files measure both decoders). The rapidyenc files exist for Linux amd64/arm64, Windows amd64 and macOS arm64; the Linux ones need glibc 2.34 or newer and libstdc++, so on Alpine take purego. Each archive contains the binary, `README.md`, `LICENSE`, `cleanHeaders.txt` and the provider samples. diff --git a/ConnPool_test.go b/ConnPool_test.go index 9eb7bae..dad185f 100644 --- a/ConnPool_test.go +++ b/ConnPool_test.go @@ -195,15 +195,15 @@ func serveScript(t *gotesting.T, reply func(line string, w net.Conn)) string { return ln.Addr().String() } -func shortIdle(t *gotesting.T) { +func shortIdle(t *gotesting.T, d time.Duration) { old := connIdleTimeout - connIdleTimeout = 300 * time.Millisecond + connIdleTimeout = d t.Cleanup(func() { connIdleTimeout = old }) } // A server that stops answering mid-session no longer blocks the caller forever. func TestIdleTimeoutEndsSilentCommand(t *gotesting.T) { - shortIdle(t) + shortIdle(t, 300*time.Millisecond) addr := serveScript(t, func(string, net.Conn) {}) // never answers c := testPool(t, addr) ci, err := c.connect() @@ -227,11 +227,13 @@ func TestIdleTimeoutEndsSilentCommand(t *gotesting.T) { } // The timeout is per read: a slow but steady article is not cut off. +// The idle timeout is 10x the gap between lines: with 300 ms it failed on a +// busy single-CPU VM (load 45) whenever one 100 ms sleep overran by 200 ms. func TestIdleTimeoutKeepsSlowTransfer(t *gotesting.T) { - shortIdle(t) + shortIdle(t, time.Second) addr := serveScript(t, func(_ string, w net.Conn) { io.WriteString(w, "222 0 body\r\n") - for i := 0; i < 30; i++ { // 3 s in total, 10x the idle timeout + for i := 0; i < 30; i++ { // 3 s in total, 3x the idle timeout time.Sleep(100 * time.Millisecond) fmt.Fprintf(w, "line %d\r\n", i) } diff --git a/README.md b/README.md index 479cee8..1e6b8ca 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,8 @@ Only one mode per process. ## Installation -1. **Download pre-built binaries** from the [Releases](../../releases) page, or +1. **Download pre-built binaries** from the [Releases](../../releases) page + (the release notes say which file to take), or 2. **Compile from source** (see Build Instructions below) 3. Configure `provider.json` with your Usenet provider details. 4. Run the program from the command line: @@ -114,6 +115,8 @@ go build . No C compiler, no cgo. Cross-compiles to any target, e.g. `CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build .` or `GOOS=darwin GOARCH=arm64`. Built on Linux with `CGO_ENABLED=0`, the binary is static (runs on glibc and musl/Alpine). +The `purego` release files are built this way: on Alpine take those, the +`rapidyenc` Linux files need glibc 2.34 or newer and libstdc++. For go-yenc-simd's AVX2/AVX-512 code on amd64, build with `GOEXPERIMENT=simd go build .` (Go 1.27; an experiment, outside the Go 1 @@ -121,6 +124,11 @@ compatibility promise). Without it the decoder still works, in plain Go. #### With rapidyenc's C decoder Needs a C/C++ compiler and cmake; the rapidyenc library is built first. +This also works on musl (Alpine: `apk add cmake g++ make linux-headers`). +Tested from source with both variants on AlmaLinux 9/10, Alpine 3.22–3.24, +Arch, CentOS Stream 9/10, Debian 13, Gentoo, openEuler 25.03, openSUSE Leap +15.6/16.0, Rocky 9/10 and Ubuntu 24.04–26.04 (amd64). With gcc older than 8 +(Leap 15.6 ships gcc 7) rapidyenc builds without its AVX-512 VBMI2 kernel. ```sh # Linux diff --git a/go.mod b/go.mod index e863c95..ce41c85 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.27.3 require ( github.com/go-while/go-cpu-mem-profiler v0.0.0-20240612221627-856954a5fc83 github.com/go-while/go-loggedrwmutex v0.0.0-20250601032232-b0aa20cdcb2c - github.com/go-while/go-yenc-simd v0.1.1 + github.com/go-while/go-yenc-simd v0.1.2 github.com/javi11/nzbparser v0.5.5 github.com/stretchr/testify v1.12.1 golang.org/x/crypto v0.58.0 diff --git a/go.sum b/go.sum index 74d0409..0d55934 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/go-while/go-cpu-mem-profiler v0.0.0-20240612221627-856954a5fc83 h1:ve github.com/go-while/go-cpu-mem-profiler v0.0.0-20240612221627-856954a5fc83/go.mod h1:31AY7BccUb7qvKi4LQCwLOYsvcza9TdQtl/zHEIOcRE= github.com/go-while/go-loggedrwmutex v0.0.0-20250601032232-b0aa20cdcb2c h1:sDQuzVfH/JKw6k06m85cWNKsB27QSH496GZeImozJic= github.com/go-while/go-loggedrwmutex v0.0.0-20250601032232-b0aa20cdcb2c/go.mod h1:PqQxwEostGAr7ARrGKLQATdk6t2CZBOSDNg8vRCmydw= -github.com/go-while/go-yenc-simd v0.1.1 h1:sEFhp92Q8xep7ypsFWG5R0sSH5ZTTVNgwRCQUvHJl5g= -github.com/go-while/go-yenc-simd v0.1.1/go.mod h1:aOlUl+t2OX3Qjj3nK2ollyEn+pkfMAsjI0upfld9tJ4= +github.com/go-while/go-yenc-simd v0.1.2 h1:OCsKQWDT+6TuuezumL0s0uQJQQ3GadGByg3vWk3mfOg= +github.com/go-while/go-yenc-simd v0.1.2/go.mod h1:aOlUl+t2OX3Qjj3nK2ollyEn+pkfMAsjI0upfld9tJ4= github.com/go-while/nzbparser v0.5.6-0.20261018190657-7c20501e8eac h1:a9UhKqxKQvKZpgy90/YilvtNaALs84/KnWuHlJ7VSOs= github.com/go-while/nzbparser v0.5.6-0.20261018190657-7c20501e8eac/go.mod h1:ikF7WI3BUGs5IHQJmKzmtTkX29NZW5nvUdo6ZWFZgL4= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= diff --git a/scripts/build-release.sh b/scripts/build-release.sh index 3c39f6d..3a96330 100755 --- a/scripts/build-release.sh +++ b/scripts/build-release.sh @@ -81,13 +81,18 @@ if [ "${DEB:-0}" = 1 ] && [ "$goos" = linux ]; then mkdir -p "$root/DEBIAN" "$root/usr/bin" "$root/usr/share/doc/nzbrex" cp "$stage/$name/$bin" "$root/usr/bin/NZBreX" cp README.md LICENSE cleanHeaders.txt provider.sample.json provider.ygg.json "$root/usr/share/doc/nzbrex/" + # purego is static; rapidyenc links glibc (symbols up to GLIBC_2.34) and libstdc++ + depends="" + if [ "$variant" = rapidyenc ]; then + depends="Depends: libc6 (>= 2.34), libstdc++6"$'\n' + fi cat >"$root/DEBIAN/control" <} +${depends}Maintainer: ${DEB_MAINTAINER:-go-while } Homepage: https://github.com/go-while/NZBreX Description: re-upload Usenet articles missing from providers NZBreX checks the articles of an NZB on your providers and re-uploads the