1P by GN⁺ | ★ favorite | 댓글 1개
  • 오래된 과학·공학 계산 자산인 Fortran 수치 코드를 브라우저로 가져오려면 WebAssembly 컴파일 경로가 필요하며, webR은 패치된 LLVM flang-new를 사용함
  • f2c, LFortran, Dragonegg, Classic Flang, LLVM Flang은 각각 현대 Fortran 지원·타깃 아키텍처·유지보수성에서 한계가 있어 아직 단순한 표준 해법이 없음
  • LLVM Flang은 wasm32-unknown-emscripten 타깃을 바로 지원하지 않아 TargetWasm32 추가가 필요하고, 런타임 연결에서는 호스트와 타깃의 long 크기 차이가 문제를 일으킴
  • 패치된 flang-new, Emscripten, Fortran 런타임 정적 라이브러리를 조합하면 Fortran 서브루틴을 C나 JavaScript에서 호출하고 PRINT, ALLOCATE, CHARACTER 인자도 처리할 수 있음
  • BLAS 3.12.0과 LAPACK 3.12.0 참조 구현을 WebAssembly 정적 라이브러리로 빌드해 손글씨 숫자 분류다항식 보간 데모를 브라우저에서 실행함

기존 Fortran 수치 코드를 브라우저로 가져오기

  • Fortran은 1957년에 등장한 오래된 언어지만, 과학·공학 계산에서 오랫동안 쓰였고 현대 Fortran은 Fortran 77의 고정 형식 제약을 대부분 벗어남
  • 목표는 현대 Fortran 루틴을 WebAssembly로 컴파일해 브라우저에서 실행하고, 수치 인자를 받아 BLAS·LAPACK 루틴을 계산한 뒤 결과를 반환하거나 콘솔에 출력하는 것임
  • 이 접근은 SciPy나 R처럼 BLAS·LAPACK에 의존하는 상위 프로그래밍 환경을 웹으로 가져올 수 있게 함
  • JavaScript나 Rust로 수치 루틴을 다시 작성하지 않고, 이미 검증된 Fortran 기반 도구와 라이브러리를 활용할 수 있음
  • webR 프로젝트는 패치된 LLVM flang-new 컴파일러로 Fortran 코드를 WebAssembly로 컴파일함
  • 현재 방식은 해킹에 의존하며, 더 경험 많은 컴파일러 개발자의 도움 없이는 변경을 LLVM에 기여하기 어려움

Fortran→WebAssembly 도구 현황

  • 2024년 기준 여러 도구와 툴체인이 있지만, 완전한 기능을 갖춘 단순한 해결책은 아직 없음
  • f2c

    • f2c는 Fortran 77을 C 코드로 변환하고, Emscripten이 이를 WebAssembly로 컴파일할 수 있게 함
    • Pyodide가 Fortran 코드가 포함된 Python 패키지를 컴파일할 때 이 방식을 사용함
    • Pyodide 로드맵에서도 이 방식은 “잘 동작하지 않는다”고 평가됨
    • 현대 Fortran 코드에는 맞지 않고, 변환 뒤에도 치명적 오류와 광범위한 패치가 필요함
  • LFortran

    • LFortran은 최근 몇 년간 기능이 크게 늘었고 WebAssembly로 바로 컴파일할 수 있음
    • 아직 알파 단계라 실제 코드 컴파일 이슈가 예상된다고 개발자들이 밝힘
    • MINPACK 같은 일부 프로젝트는 컴파일할 수 있지만, 전체 Fortran 명세를 지원하지 않아 더 큰 프로젝트는 실패할 수 있음
    • 개발 목표는 Fortran 2018 전체 지원이며, Jupyter와 비슷한 대화형 Fortran REPL이 두드러진 기능임
  • Dragonegg

    • Dragonegg는 GCC 프런트엔드를 사용해 LLVM IR을 내보내는 GCC 플러그인임
    • LLVM 백엔드로 WebAssembly 출력을 만들 수 있고, webR이 처음 Fortran 소스를 WebAssembly로 컴파일할 때 사용한 방식임
    • 최신 지원 버전이 gcc-4.8llvm-3.3이라 매우 오래된 GCC·LLVM이 필요함
    • 대부분 사용자는 VM이나 Docker 컨테이너가 필요하고, Dragonegg가 내보내는 LLVM IR도 WebAssembly 출력을 위해 추가 후처리를 거쳐야 함
    • 2020년에는 Fortran 코드를 WebAssembly로 컴파일하는 실질적인 방법이 사실상 이것뿐이었음
  • Classic Flang

    • Classic Flang은 오픈소스화된 PGI/NVIDIA pgfortran 기반 LLVM 대상 Fortran 컴파일러임
    • 32비트 출력을 지원하지 않아 wasm32 타깃에는 사용할 수 없음
    • Firefox, Chrome, Node는 작성 시점에 wasm64를 지원하지만 기능 플래그 뒤에 잠겨 있음
    • 프로젝트 문서도 새 프로젝트에서 Classic Flang을 선택하는 것이 좋은 생각은 아닐 수 있다고 안내함
  • LLVM Flang

    • LLVM Flang은 LLVM용 Fortran 프런트엔드를 처음부터 다시 구현한 프로젝트이며, LLVM 11부터 LLVM 프로젝트에 포함됨
    • 아직 프로덕션 준비 완료로 간주되지는 않지만, flang-new의 사전 프로덕션 버전은 실제 Fortran 코드 컴파일에 꽤 쓸 수 있는 수준이 됨
    • 기본 상태에서는 WebAssembly 출력을 만들 수 없음
    • LLVM의 모듈식 설계를 이용하면 Flang 프런트엔드와 LLVM WebAssembly 백엔드를 함께 사용할 수 있음
    • 2020년에도 가능했지만 더 큰 LLVM 패치, 사용자 정의 수학 루틴 주입, 다단계 컴파일 과정이 필요했음
    • 현재는 flang-new 프런트엔드 개발 덕분에 LLVM 소스의 작은 변경만으로 Fortran→WebAssembly 컴파일러를 만들 수 있음

LLVM Flang을 WebAssembly용으로 빌드하기

  • 패키지 매니저로 설치한 LLVM에는 flang-new 바이너리가 포함되지 않을 수 있음
    • 예시로 macOS Homebrew의 LLVM v17.0.6에서는 flang-new 명령을 찾지 못함
  • LLVM Flang 소스를 수정해야 하므로 LLVM v18.1.1 소스를 직접 빌드함
  • CMake 설정은 기본 타깃 트리플을 wasm32-unknown-emscripten으로 두고, WebAssembly 타깃과 clang;flang;mlir 프로젝트를 활성화함
  • 빌드가 끝나면 build/bin/flang-new --version에서 다음 정보를 확인할 수 있음
    • 버전: flang-new version 18.1.1
    • 타깃: wasm32-unknown-emscripten
    • 스레드 모델: posix

첫 번째 문제: wasm32 타깃 미구현

  • 단순 Fortran 서브루틴 foo.f08flang-new로 컴파일하면 다음 오류가 발생함
    • not yet implemented: target not implemented
  • 원인은 flang-newwasm32-unknown-emscripten 타깃 트리플이 아직 구현되어 있지 않기 때문임
  • 해결책은 flang/lib/Optimizer/CodeGen/Target.cppTargetWasm32 타깃 특성을 추가하는 패치임
    • 기본 폭을 32로 설정함
    • 복소수 인자와 반환 타입을 WebAssembly 쪽 LLVM 타입으로 변환하는 마샬링을 정의함
    • llvm::Triple::ArchType::wasm32 분기에 TargetWasm32를 연결함
  • 패치 후 다시 빌드하면 Fortran 소스가 WebAssembly 객체로 컴파일됨
    • file foo.o 결과: WebAssembly (wasm) binary module version 0x1 (MVP)
    • llvm-nm foo.o에서 foo_ 심볼 확인 가능

C와 JavaScript에서 Fortran 서브루틴 호출하기

  • Fortran 루틴은 일반적으로 인자를 참조로 전달하며, INTENT()로 인자의 사용 방식을 선언할 수 있음
  • 예시 Fortran 서브루틴 foox, y, z 정수 인자를 받아 z = x + y를 수행함
  • 네이티브 빌드에서 gfortran -c foo.f08 -o foo.o를 실행하면 심볼 이름이 foo_처럼 뒤에 밑줄이 붙을 수 있음
  • C에서 호출할 때는 외부 심볼을 extern void foo_(int*, int*, int*);처럼 선언하고, 인자를 주소로 전달함
  • WebAssembly에서는 flang-new로 만든 foo.o를 Emscripten으로 C 코드와 연결할 수 있음
    • emcc main.c foo.o -o main.js
    • node main.js 출력: 1 + 1 = 2
  • JavaScript 직접 호출

    • C 코드 없이 JavaScript에서 Fortran 서브루틴을 직접 호출할 수도 있음
    • Emscripten 링크 단계에서 _foo_, _malloc, _free를 내보냄
    • emcc foo.o -sEXPORTED_FUNCTIONS=_foo_,_malloc,_free -o foo.js
    • JavaScript는 Module._malloc()으로 정수 저장 공간을 할당하고, Module.HEAPU32에 값을 쓴 뒤 Module._foo_(x, y, z)를 호출함
    • 실행 결과는 다음과 같음
    • x = 123
    • y = 456
    • x + y = 579
    • 브라우저에서는 foo.jsstandalone.js를 HTML에서 로드해 같은 결과를 JavaScript 콘솔에서 볼 수 있음

두 번째 문제: Fortran 런타임 라이브러리

  • PRINT *, "Hello, World!"를 포함한 Fortran 서브루틴을 빌드하면 링크 단계에서 런타임 심볼이 없다는 오류가 발생함
    • _FortranAioBeginExternalListOutput
    • _FortranAioOutputAscii
    • _FortranAioEndIoStatement
  • 원인은 LLVM Fortran 런타임 라이브러리를 아직 WebAssembly용으로 컴파일하지 않았기 때문임
  • 런타임 라이브러리는 LLVM 소스 트리의 llvm-project/flang/runtime에 C++로 작성되어 있음
  • Emscripten의 em++emar로 런타임 소스를 빌드하면 정적 라이브러리 build/flang/runtime/libFortranRuntime.a를 만들 수 있음
  • 이 라이브러리를 링크하면 Hello, World! 빌드는 진행되지만, 처음에는 함수 시그니처 불일치 경고가 발생함

세 번째 문제: 호스트와 타깃의 long 크기 차이

  • hello.o와 Fortran 런타임 라이브러리를 연결하면 _FortranAioOutputAscii 시그니처 불일치 경고가 나옴
    • Fortran 객체 쪽은 (i32, i32, i64) -> i32를 기대함
    • Emscripten으로 빌드한 런타임 쪽은 (i32, i32, i32) -> i32로 정의됨
  • WebAssembly는 여러 컴파일 단위에 걸쳐 정의된 심볼의 인자와 반환 타입이 일관되어야 함
  • 이 문제는 경고로 끝나지 않고, Node 실행 시 RuntimeError: unreachable로 실패함
  • LLVM Flang의 RTBuilder.h에는 sizeof 사용이 build == host == target을 가정한다는 TODO 주석이 있음
  • 현대 64비트 Unix 계열 호스트에서는 sizeof(long)이 8바이트지만, wasm32-unknown-emscripten 타깃에서는 4바이트여야 함
  • Fortran의 CHARACTER 타입 인자를 함수나 서브루틴에 전달할 때 문자열 길이를 넘기는 숨은 인자가 추가될 수 있음
  • Fortran 런타임 라이브러리에서 이 길이 인자는 size_t로 선언되고, typedef 체인을 거쳐 unsigned long과 같아짐
  • 이 숨은 인자의 크기 차이가 i64i32 불일치를 일으킴

임시 패치: 4바이트 값 강제

  • 이상적인 해결책은 flang-new가 크로스 컴파일 시 호스트와 무관하게 타깃 아키텍처와 데이터 모델에 맞는 i32 또는 i64를 내보내는 것임
  • 현재는 wasm32와 Emscripten에 맞춰 long 크기를 4바이트로 하드코딩하는 패치를 사용함
  • 패치 내용은 두 갈래임
    • RTBuilder.h에서 longunsigned long의 모델 타입을 8 * sizeof(...) 대신 8 * 4로 강제함
    • CodeGen.cpp에서 malloc() 호출 인자를 64비트 대신 32비트 정수로 생성하고, 할당 크기를 i32로 캐스팅함
  • 이 변경으로 Fortran 90에 도입된 ALLOCATE() 기반 동적 할당도 고쳐짐
  • 재빌드 후 hello.f08hello.c를 런타임 라이브러리와 연결하면 경고 없이 빌드되고, Node에서 다음 출력이 나옴
    • Hello, World!

BLAS를 WebAssembly로 빌드하기

  • BLAS는 행렬·벡터 곱셈 등 선형대수의 공통 연산을 수행하는 저수준 루틴 집합임
  • 원래 BLAS 루틴은 1979년에 공개되었고, 수치 계산 분야의 사실상 표준임
  • 참조 구현 BLAS 3.12.0은 Fortran 90으로 작성되어 있으며, netlib에서 받을 수 있음
  • make.inc에서 다음 도구를 지정해 빌드함
    • FC = ../build/bin/flang-new
    • FFLAGS = -O2
    • AR = emar
    • RANLIB = emranlib
  • 빌드 결과 blas_LINUX.a 정적 라이브러리가 만들어짐
  • 예시 Fortran 루틴 bar는 BLAS level 2 루틴 ZGEMV()를 호출함
  • ZGEMV()는 복소수 행렬-벡터 연산을 수행하며, 예제는 COMPLEX(KIND=8) 인자와 CHARACTER 설정 인자 'N'을 사용함
  • C 프로그램에서 복소수 배열을 만들고 Fortran 루틴에 전달한 뒤 결과를 출력함
  • Node 실행 결과는 다음과 같음
    • Y[0]: 23.000000 + 6.000000i
    • Y[1]: 18.000000 + 10.000000i
    • Y[2]: 6.000000 + 16.000000i
  • 이 결과로 Fortran 90 소스에서 컴파일한 BLAS가 WebAssembly 아래에서 실행됨을 확인함

브라우저 예제: 손글씨 숫자 분류기

  • 데모는 다층 퍼셉트론(MLP) 인공신경망으로 손으로 그린 0–9 숫자를 분류함
  • 사용자는 마우스나 터치스크린으로 숫자를 그릴 수 있고, 네트워크가 예측한 상대 확률이 오른쪽 플롯에 표시됨
  • 모델 가중치는 Python으로 사전 학습되었지만, 분류는 실행 시점에 JavaScript와 WebAssembly로 브라우저에서 수행됨
  • MLP 분류 과정은 본질적으로 행렬-벡터 덧셈과 곱셈의 반복임
  • 무거운 계산은 Fortran 서브루틴 하나가 BLAS level 2 루틴 DGEMV()를 사용해 처리함

LAPACK을 WebAssembly로 빌드하기

  • LAPACK은 선형대수 문제를 수치적으로 푸는 소프트웨어 라이브러리이며, BLAS 위에 구축됨
  • 참조 구현 LAPACK 3.12.0은 netlib에서 제공되며 수정 BSD 라이선스로 배포됨
  • make.inc.examplemake.inc로 복사한 뒤 다음 설정을 변경함
    • FC를 빌드한 flang-new의 전체 경로로 지정함
    • FFLAGS = -O2
    • AR = emar
    • RANLIB = emranlib
    • TIMER = INT_CPU_TIME
  • make lib 명령으로 WebAssembly 정적 라이브러리 liblapack.a를 생성함
  • 이후 LAPACK 루틴은 BLAS 예제와 비슷한 방식으로 호출할 수 있음

브라우저 예제: 선형대수를 이용한 다항식 보간

  • 데모는 점들의 집합에 대해 보간 다항식을 찾으며, LAPACK 루틴이 브라우저에서 실행됨을 보여줌
  • 사용자가 플롯을 클릭해 새 점을 추가하면, 모든 점을 지나는 보간 다항식을 찾음
  • 방법은 Vandermonde’s method를 사용함
  • 이 방식으로 얻은 선형대수 방정식은 LAPACK의 DGELS() 루틴으로 수치적으로 풂
  • n개의 데이터 포인트를 정확히 포함하는 n-1차 다항식은 항상 찾을 수 있음
  • n이 커지면 다항식이 연속한 데이터 포인트 사이에서 크게 요동치는 Runge 현상이 생길 수 있고, 스플라인 보간으로 피할 수 있음

남은 과제와 제공 도구

  • 패치된 LLVM Flang을 사용하면 현대 Fortran 코드를 WebAssembly 객체로 컴파일할 수 있음
  • 이 방식의 장점은 웹용 수치 알고리듬을 JavaScript로 새로 작성하지 않고, 이미 존재하는 Fortran 도구와 라이브러리를 사용할 수 있다는 점임
  • flang-new가 WebAssembly를 공식 지원하면 webR와 R 패키지를 위해 LLVM 포크를 유지하는 부담이 줄어듦
  • 현재는 LLVM Flang과 크로스 컴파일 문제를 모든 타깃에서 제대로 고칠 더 나은 경로가 필요함
  • LLVM Flang을 직접 빌드하기 어렵거나 원하지 않는 사용자를 위해 GitHub Container Registry에 패치된 LLVM Flang 바이너리 Docker 컨테이너가 제공됨

댓글과 토론

Hacker News 의견들
  • 배경을 조금 보태면, 이 Fortran 탐구는 George가 R을 브라우저에서 실행하려고 진행 중인 훌륭한 WebR 작업의 일부임
    R 소스에는 Fortran 코드가 꽤 많고, WebR은 원래 f2c로 Fortran을 C로 먼저 컴파일한 뒤 그 C를 wasm으로 컴파일했던 것으로 앎
    LLVM Flang 패치 덕분에 WebR을 진짜 Fortran 컴파일러로 빌드할 수 있게 됨
    George가 블로그 글에서 직접 말하진 않았지만, Flang이 이 패치를 받아들이거나 더 나은 방식으로 구현하길 바란다고 말한 적이 있음
    그렇게 되면 별도 패치 유지가 필요 없어지고, 수정 없는 Flang이 wasm으로 컴파일할 수 있어 Fortran을 쓰는 다른 프로젝트에도 이득임
    https://docs.r-wasm.org/webr/latest/

    • 풀 리퀘스트는 언제나 환영이고(https://github.com/llvm/llvm-project), 도움이 필요하면 LLVM Fortran 개발 커뮤니티(https://discourse.llvm.org/c/subprojects/flang/33)에 연락할 수 있음
      다만 나는 Nvidia의 Fortran 제품 개발 완료에 필요한 일에 집중하고 있어서, 이런 작업에 쓸 시간이 남아 있지 않음
    • 소스 간 변환으로 F77을 JavaScript로 옮기는 것도 이미 꽤 괜찮지만, WASM이 더 낫다
  • 20년 전 Xilinx에서 FORTRAN 컴파일 작업을 했었는데, 기억나는 건 f2c.h 헤더 파일에 barf 정의가 있었다는 것뿐임
    /* f2c.h -- Standard Fortran to C header file /
    /* barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed."
    (https://www.netlib.org/clapack/f2c.h)

    • 전부 대문자로 FORTRAN이라고 쓰는 게 나에 대해 뭔가 말해주는 걸까? 내 눈에는 Fortran이 어색해 보임
  • 설명 방식으로 가장 단순한 비자명 예제를 쓰는 게 정말 좋음
    글이 “JavaScript에서 BLAS 함수를 호출한다”는 구체적인 문제에 기반해 있어서 많이 배운 느낌이고, 훌륭한 글임

  • 감탄해야 할지 경악해야 할지 모르겠고, 아마 둘 다인 듯함
    f18을 빌드할 때는 llvm-project/main 최신 소스를 쓰는 걸 추천함
    프로젝트가 빠르게 움직이고 있어서 이미 고쳐진 문제를 디버깅하거나 이미 구현된 기능을 놓치면 시간 낭비가 됨

    • LLVM 소스를 충분히 이해하지 못해서 요지를 파악하기 어려웠음
      WebAssembly 포트를 작업해서 중간 코드가 Fortran까지 동작하는 지점으로 가게 하려는 건가?
  • WebAssembly 개발은 잘 모름
    소비자 입장에서 WebAssembly가 지금 당장 제공하는 게 있나? 아니면 프로그램이 진정으로 이식 가능해지는 미래를 위한 기반 작업에 가까운가?
    WebAssembly 장치가 네트워크나 파일 같은 접근 제한을 더 쉽게 해준다는 얘기는 들었지만, 이게 이론인지 이미 구현된 건지는 모르겠음

    • 기본적으로 Wasm은 가상 머신이고, 이식 가능하다는 점에서 JVM과 매우 비슷함
      핵심 차이는 Wasm 자체에는 표준 라이브러리도 없고 입출력 함수도 노출하지 않는다는 것임
      그래서 호스트, 즉 VM을 직접 만들고 Wasm 바이너리가 가져다 쓸 함수를 노출할 수 있으며, Wasm 바이너리는 오직 그 함수들을 통해서만 외부 세계에 접근 가능함
      또 장점은 바이너리 형식이 독점이 아니고 명세가 있어서 누구나 Wasm VM을 구현할 수 있다는 점임
      다만 지금은 아직 좋은 상태라고 보긴 어렵고, 너무 이르며 W3C와 비슷한 그룹에서 표준화 중인 새 기능이 많고 절차도 매우 느림
    • 개발자이거나 제품을 배포하는 입장에서 견고한 샌드박싱을 원한다면, WASM이 지금 사용할 수 있는 최선의 선택지에 가까움
      대부분의 대상에 배포하거나 교차 컴파일할 방법도 있음
    • 제대로 구현됐다면 고객 입장에서는 눈치채지 못해야 함
      컴퓨터 CPU가 ARM인지 x86인지 보통 모르고 크게 신경 쓰지 않는 것과 비슷함
      그래서 네이티브 코드로 도는지, JVM·.NET·WASM 같은 VM 위에서 도는지 같은 세부사항에 관심이 없다면 다른 해법보다 뭘 더 제공하는지 말하기 어렵다
      보통은 나쁜 사례만 눈에 띄고, 그게 “모든 Electron 프로그램은 부풀고 자원 먹는 괴물이고, 모든 네이티브 앱은 자동으로 효율적인 소프트웨어 공학의 경이” 같은 밈으로 변함
  • 1981/82년에 썼던 Fortran 78 코드를 보관해뒀다면 여기서 돌아가는지 시험해볼 수 있었을 텐데 아쉬움
    Jovial 프로그래밍 언어 소스 코드 포매터였고, Fortran으로 할 만한 일은 아니었지만 그때는 선택지가 그것뿐이었음

    • Orange County의 Hughes에서 일했었나?
  • JavaScript에서 쓸 만한, 어느 정도 프로덕션 준비가 된 선형대수 생태계가 있나?
    검색해보면 대개 10년쯤 된 익숙한 라이브러리의 JavaScript 포트, 예를 들어 emscripten 경유 포트만 나오는데 뭔가 놓치고 있는지 궁금함

    • WebGPUWebNN 위에 BLAS에 해당하는 게 있나?
  • LFortran을 더 깊게 다루지 않는 게 이상함
    온라인에서 돌아가는 훌륭하고 놀라운 WASM 예제도 있음
    https://dev.lfortran.org/

    • 글에는 LFortran 컴파일러가 지난 몇 년간 크게 발전했다고 쓰여 있음
      2020년에는 기능이 많이 빠져 있었고 Fortran의 아주 작은 부분집합만 지원했지만, 지금은 훨씬 넓은 언어 기능을 지원하고 꽤 많은 Fortran 코드를 컴파일할 수 있음
      WebAssembly로도 기본 컴파일 가능함
      다만 아직 LFortran을 쓰기에는 거친 부분이 있고, 프로젝트는 현재 알파 단계로 간주되며 실제 코드 컴파일에서 문제가 생길 수 있다고 개발자들이 밝힘
      MINPACK 같은 일부 프로젝트는 성공적으로 컴파일할 수 있지만, Fortran 전체 명세를 아직 지원하지 않아서 더 큰 프로젝트 상당수는 여전히 컴파일할 수 없음
      LFortran 개발자들은 Fortran 2018 완전 지원을 목표로 하고 있고, 눈에 띄는 기능은 Jupyter 같은 대화형 Fortran REPL임
      몇 년 더 개발되면 WebAssembly용 Fortran 코드 컴파일에 훌륭한 선택지가 될 것으로 봄
      https://dev.lfortran.org의 LFortran 데모를 보라고 되어 있는데, 매우 인상적이지만 내가 처음 해본 x * 2x * 3으로 바꾸는 작업은 현재 코드 생성기가 지원하지 않았음
  • .NET과 Java 위의 Fortran도 있음
    https://www.silverfrost.com/14/ftn95/ftn95_fortran_95_for_microsoft_dotnet_features.aspx
    https://dl.acm.org/doi/10.1145/376656.376833

  • https://medium.com/@tomasreimers/compiling-tensorflow-for-the-browser-f3387b8e1e1c 작업을 할 때 TensorFlow가 Fortran으로 작성된 인기 수학 라이브러리인 BLAS/Lapack이 아니라 Eigen을 써서 정말 다행이라고 생각했음
    아니었다면 일이 훨씬 많아졌을 것임