Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Rindow Openblas Ffi Laravel Package

rindow/rindow-openblas-ffi

PHP FFI interface to OpenBLAS and math libraries, plus a universal buffer for exchanging C data with PHP. Enables fast N-dimensional array operations with Rindow Math Matrix, useful for deep learning in PHP.

View on GitHub
Deep Wiki
Context7

Getting Started

Treat this as a native-extension install with a Composer wrapper, not a normal package. The Composer step takes seconds; the native library is the actual work.

1. Install the native library first.

# Linux (pthread build — see Gotchas before choosing OpenMP)
sudo apt install libopenblas0-pthread liblapacke

# Windows: unzip the OpenBLAS release, then add its bin/ to PATH
# macOS: vecLib ships with the OS, nothing to install

2. Verify FFI is actually on before touching Composer. This is the single most common first-run failure:

php -m | grep -i ffi          # must list "FFI"
php -i | grep ffi.enable      # must be "1" (or "preload")

In php.ini: ffi.enable=1 for CLI/queue workers. If you plan to use it under php-fpm at all, ffi.enable=preload is the safer setting, but see the Gotchas — most teams should keep this out of the request path entirely.

3. Install and smoke-test.

composer require rindow/rindow-openblas-ffi
php artisan tinker
$blas = new Rindow\OpenBLAS\FFI\Blas();
$buf  = new Rindow\Math\Buffer\FFI\Buffer(4, 'double', [1.0, 2.0, 3.0, 4.0]);
echo $buf->bytesize();   // 32

If that constructor throws or segfaults, the native library name/path is wrong — fix that before writing any real code.

4. Where to look first. The README is thin and mostly installation. The real documentation is vendor/rindow/rindow-openblas-ffi/src/:

  • FFI/Blas.php and the corresponding interface — the BLAS routines exposed (gemm, gemv, dot, axpy, scal, nrm2, trsv, rot*).
  • FFI/Lapack.php — factorizations and solvers (gesvd, potrf, getrf, …).
  • tests/ — the argument-order and leading-dimension conventions are demonstrated far more reliably here than in prose.

Read the sibling package rindow/rindow-math-matrix before picking an API. For 90% of Laravel work you want MatrixOperator (ND arrays) and should only drop to raw BLAS for hot kernels.

5. First real use case. The canonical win is replacing a nested-loop pure-PHP numeric routine over a batch: bulk cosine similarity across an embedding table, a covariance matrix for a portfolio calc, or an SVD for a recommender. Start there — one queued job, one fixed input size, one number you can cross-check against a pure-PHP reference implementation.

// app/Services/LinearAlgebra/SimilarityService.php
final class SimilarityService
{
    public function __construct(
        private readonly \Rindow\Math\Matrix\MatrixOperator $mo,
    ) {}

    /** @param list<list<float>> $matrix  @param list<float> $query */
    public function cosineScores(array $matrix, array $query): array
    {
        $a = $this->mo->array($matrix);
        $q = $this->mo->array($query);
        // NDArray ops here; see rindow-math-matrix docs for the exact calls.
        // The point: rows come back as one buffer, not a PHP loop.
        return $this->mo->toArray($this->mo->matmul($a, $q));
    }
}

Verify argument order against the interface in src/ — signatures have shifted across minor releases.

Implementation Patterns

Bind it as a singleton, resolve interfaces. FFI handles are expensive to construct and the native library load should happen once per process.

// app/Providers/NumericsServiceProvider.php
public function register(): void
{
    $this->app->singleton(\Rindow\OpenBLAS\FFI\Blas::class, fn () => new \Rindow\OpenBLAS\FFI\Blas());
    $this->app->singleton(\Rindow\OpenBLAS\FFI\Lapack::class, fn () => new \Rindow\OpenBLAS\FFI\Lapack());
    $this->app->singleton(\Rindow\Math\Matrix\MatrixOperator::class);
}

Never let Buffer escape your service layer. Rindow\Math\Buffer\FFI\Buffer is a raw 1-D typed allocation. It is not serializable, not JSON-encodable, and a stale reference keeps native memory alive. Your public boundary should be array in, array out. Everything below that is buffers.

Batch jobs, not requests. The right shape is a queued job with a fixed-size chunk:

final class RecomputeEmbeddingScores implements ShouldQueue
{
    public int $timeout = 300;
    public function __construct(public readonly int $batchId) {}

    public function handle(SimilarityService $svc): void
    {
        foreach (LazyCollection::make(fn () => Embedding::where('batch_id', $this->batchId)->cursor())->chunk(2000) as $chunk) {
            $scores = $svc->cosineScores($chunk->pluck('vector')->all(), $this->query());
            // persist scores...
        }
    }
}

Allocate once, reuse across iterations. Every new Buffer(...) and every toArray() is a copy. In a loop over 10k rows, that dominates runtime and completely erases the reason you adopted this package.

$out = new Buffer($rows, 'double');       // allocate outside
for ($i = 0; $i < $rows; $i++) {
    $blas->gemv(/* ... writes into $out at offset $i ... */);
}

Prefer matrix-vector/matrix-matrix over per-row calls. One gemm on a 2000×768 matrix beats 2000 dot calls by a wide margin — you are amortizing the FFI boundary crossing, which is the dominant cost at small sizes.

Keep a pure-PHP reference implementation for tests. Assert with a tolerance, not equality:

$this->assertEqualsWithDelta($expected, $actual, 1e-9);

Pin the version. "rindow/rindow-openblas-ffi": "1.0.7" in composer.json — not ^1.0. Each patch adds or narrows routines (see Gotchas), and a routine-level behavior change will not show up in your test suite unless you wrote a contract test for that specific routine.

Where it fits architecturally. Offline/batch numerics: nightly recommendation recompute, PCA on feature matrices, regression solves, signal processing on uploaded files, risk calculations. It does not fit in a synchronous HTTP handler — route it through a job and poll/notify.

Gotchas and Tips

A segfault is not an exception. Bad lda, an out-of-bounds offset, or a type mismatch in the FFI signature kills the PHP process instantly. Your queue worker dies with no stack trace in laravel.log, no failed-job record, and Supervisor just restarts it — potentially in a loop, re-running the same poisoned payload. Mitigations: cap $tries, validate buffer bounds before every call, and test new routines in a standalone CLI script before wiring them into a job.

Column-major and lda are silent-wrong-answer territory. OpenBLAS is Fortran-order. The leading-dimension parameter must be the stride, not the row count. This produces plausible-looking wrong numbers rather than an error. Cross-check every new call against a tiny hand-computed 2×2 or 3×3 case.

Linux: pthread, not OpenMP. The README is explicit — the OpenMP OpenBLAS conflicts with rindow-matlib and is "unstable and slow." If OpenMP is already installed:

sudo apt install libopenblas0-pthread liblapacke
sudo apt remove libopenblas0-openmp
# or: sudo update-alternatives --config libopenblas.so.0-x86_64-linux-gnu

Since 1.0.3 the loader only accepts libopenblas.so.0 and liblapacke.so.3. Installing libopenblas-dev (which provides the unversioned .so symlinks) does nothing for you and can confuse diagnosis. Check with ldd / ldconfig -p | grep openblas.

Thread oversubscription. OpenBLAS spawns its own thread pool. Ten Horizon workers each spawning N threads on an 8-core box will thrash. Set threads to 1 per worker and let the queue concurrency do the parallelism, or run numerics on a dedicated queue with numprocs=1 and higher concurrency.

Memory is manual in spirit even if PHP owns it. Buffers are freed by the GC, not by scope exit in a predictable way. In long-running workers, unset($buf) plus an occasional gc_collect_cycles() after each batch prevents RSS creep. Watch for int overflow on very large element counts.

1.0.7 narrowed an API. Complex-number support was removed from rot and added to rotg; trsv was added. If anything in your codebase passes complex operands to rot, that upgrade is breaking. Grep before bumping. Also note trsv is an in-place triangular solve — it mutates its input buffer, which is a classic aliasing bug when you reuse a buffer across rows.

FFI and opcache/JIT interact. ffi.enable=preload requires the FFI calls to be in preloaded files, and JIT settings can affect FFI call performance and occasionally stability. If you see intermittent crashes under php-fpm, test with JIT off before assuming the native library is at fault.

Failure mode to design for: FFI unavailable. Shared hosting, hardened Docker images, and some managed PHP platforms disable FFI. Fail fast and loudly at boot rather than deep inside a job:

abort_unless(extension_loaded('ffi') && ini_get('ffi.enable'), 500, 'FFI required for numerics');

Build the swap hatch on day one. Wrap everything behind your own interface (SimilarityService, LinearAlgebraService) with a pure-PHP or out-of-process (Python/NumPy microservice) fallback. With 2 stars and 0 dependents, bus-factor is the real long-term risk — a thin internal interface costs you an hour now and makes a fork or replacement a non-event later. rindow-math-matrix already ships a plain-PHP driver, which is the cheapest fallback to wire in.

Debugging toolkit. Work in php artisan tinker or a standalone script, not through the HTTP stack. Log the exact arguments (shape, lda, offsets, increments) before each call once, then remove. Compare against a pure-PHP implementation on fixed seed data. And read the package's tests/ when a call misbehaves — the tests are the actual specification.

Extension points worth knowing. The MatrixOperator/BufferFactory/Blas/Lapack constructors are the composition seams — Rindow's driver system expects those to be swappable, which is exactly where you inject a fake in tests. That's also where you'd slot in a different BLAS provider if you ever outgrow this one.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings