减少AWS Lambda部署的scipy和numpy大小

问题描述 投票:8回答:2

我正在尝试在aws lambda上部署python应用程序。它具有几个大的python依赖项,最大的依赖项是scipy和numpy。结果是我的应用程序明显大于允许的250MB。

[试图找到一种减小尺寸的方法时,我遇到了这里详细介绍的方法:

https://github.com/szelenka/shrink-linalg

本质上,当使用pip安装时,在scipy&numpy cython编译期间,可以将标志传递给c编译器,这会将调试信息保留在已编译的c二进制文件中。结果是scipy和numpy减少到原始大小的大约50%。我能够在本地运行它(ubuntu 16.04),并且创建二进制文件没有问题。使用的命令是:

CFLAGS="-g0 -I/usr/include:/usr/local/include -L/usr/lib:/usr/local/lib" pip install numpy scipy --compile --no-cache-dir --global-option=build_ext --global-option="-j 4"

问题是,要在aws lambda上运行,必须在与运行lambda的环境相似的环境中编译二进制文件。您可以在此处找到环境图片:

https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html

在ec2实例上加载映像后,我在安装一些依赖项后尝试运行相同的pip安装

sudo yum install python36 python3-devel blas-devel atlas atlas-devel lapack-devel atlas-sse3-devel gcc gcc-64 gcc-gfortran gcc64-gfortran libgfortran, gcc-c++ openblas-devel python36-virtualenv

numpy编译正常,但是scipy编译不好。 cython不会引起任何问题,但是fortran编译会引起问题。我收到以下错误:

error: Command "/usr/bin/gfortran -Wall -g -Wall -g -shared build/temp.linux-x86_64-3.6/build/src.linux-x86_64-3.6/scipy/integrate/_test_odeint_bandedmodule.o build/temp.linux-x86_64-3.6/build/src.linux-x86_64-3.6/build/src.linux-x86_64-3.6/scipy/integrate/fortranobject.o build/temp.linux-x86_64-3.6/scipy/integrate/tests/banded5x5.o build/temp.linux-x86_64-3.6/build/src.linux-x86_64-3.6/scipy/integrate/_test_odeint_banded-f2pywrappers.o -L/usr/lib64/atlas -L/usr/lib/gcc/x86_64-amazon-linux/6.4.1 -L/usr/lib/gcc/x86_64-amazon-linux/6.4.1 -L/usr/lib64 -Lbuild/temp.linux-x86_64-3.6 -llsoda -lmach -llapack -lptf77blas -lptcblas -latlas -lptf77blas -lptcblas -lpython3.6m -lgfortran -o build/lib.linux-x86_64-3.6/scipy/integrate/_test_odeint_banded.cpython-36m-x86_64-linux-gnu.so -Wl,--version-script=build/temp.linux-x86_64-3.6/link-version-scipy.integrate._test_odeint_banded.map" failed with exit status 1

我尝试重新安装gfortran以及整个gcc集合,但是没有任何运气。不幸的是,我对fortran编译器的经验非常有限。如果有人有任何想法,或者有c二进制的编译版本,我将不胜感激。

amazon-web-services scipy aws-lambda cython gfortran
2个回答
0
投票

[好,所以,我解决了这个问题,尽管很怪癖。

我传递给pip的标志是为了减少c依赖项的大小,而不是fortran依赖项的大小。因此,使用通常通过pip下载的预编译的fortran依赖项确实没有问题。

因此,我首先在文件夹sp中创建了未更改的scipy软件包的参考版本:

pip install scipy -t sp

然后,我创建了一个go程序来充当gfortran编译器的包装(或者从技术上讲是/ usr / bin中gfortran编译器的链接)

package main

import "os"
import "strings"
import "io/ioutil"
import "log"
import "os/exec"
import "fmt"


func checkErr(err error) {
    if err != nil {
        log.Fatal(err)
    }
}


func exists(path string) (bool, error) {
    _, err := os.Stat(path)
    if err == nil { return true, nil }
    if os.IsNotExist(err) { return false, nil }
    return true, err
}


func copyr(src string, dst string) {
    // Read all content of src to data
    data, err := ioutil.ReadFile(src)
    checkErr(err)
    // Write data to dst
    err = ioutil.WriteFile(dst, data, 0644)
    checkErr(err)
}


func main() {

    search_folder := "/home/ec2-user/sp/scipy"
    wrapped_compiler := "/usr/bin/inner_gfortran"
    argsWithProg := os.Args
    noProg := os.Args[1:]
    primed := 0
    check := "-o"
    var (
        cmdOut []byte
        err    error
    )
    for _, el := range argsWithProg {
        if primed == 1{
            primed = 0
            s := strings.Split(el, "scipy")
            if len(s) != 2{
                continue
            }
            src := search_folder + s[1]
            src_exi, _ := exists(src)
            if src_exi == false {
                continue
            }
            primed = 2
            dir_parts := strings.Split(el, "/")
            dir_parts = dir_parts[:len(dir_parts)-1]
            dir := strings.Join(dir_parts,"/")
            exi, _ := exists(dir)
            if exi == false {
                os.MkdirAll(dir, os.ModePerm)
            }
            os.Create(el)
            copyr(src, el)

        }
        if el == check{
            primed = 1
        }
    }
    if primed == 0 {
        if cmdOut, err = exec.Command(wrapped_compiler, noProg...).Output(); err != nil {
            fmt.Fprintln(os.Stderr, "There was an error running fortran compiler: ", err)
            os.Exit(1)
        }
        os.Stdout.Write(cmdOut)
    }

}

将实际编译器移至inner_gfortran

sudo mv /usr/bin/gfortran /usr/bin/inner_gfortran

然后将go包装器放回原处

包装器将把大多数指令传递给编译器,但是如果指令是将fortran程序编译为scipy,并且编译后的二进制文件已经存在于我的scipy参考版本中,则包装器只需将参考版本复制到新版本中正在编译。

然后做到了。减小大小的scipy和numpy版本现在可以在适用于python 3.6的aws lambda上使用。


0
投票

我知道您提出这个问题已经有一段时间了,但这也许会对其他人有所帮助。

您可以使用类似lambda的docker容器来编译资源,然后将库复制回您的开发环境。使用这些编译文件作为lambda资源

这篇文章对我有很大帮助:https://medium.com/@mohd.lutfalla/how-to-compile-resources-for-aws-lambda-f46fadc03290

© www.soinside.com 2019 - 2024. All rights reserved.