Уменьшение размера scipy и numpy для развертывания aws lambda - PullRequest
0 голосов
/ 13 ноября 2018

Я пытаюсь развернуть приложение на Python на AWS Lambda.У него есть несколько больших зависимостей Python, самые большие из которых scipy и numpy.В результате мое приложение значительно больше разрешенных 250 МБ.

Пытаясь найти способ уменьшить размер, я столкнулся с подходом, подробно изложенным здесь:

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-лямбде двоичные файлы должны быть скомпилированы в среде, аналогичной той, на которой работает лямбда-версия.Образ среды можно найти здесь:

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 не вызывает никаких проблем, но компиляция на Фортране есть.Я получаю следующую ошибку:

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, но безуспешно.К сожалению, у меня очень ограниченный опыт работы с компиляторами фортрана.Если у кого-то есть идеи или скомпилированная версия двоичных файлов, я был бы весьма признателен.

1 Ответ

0 голосов
/ 13 ноября 2018

Итак, я решил это, хотя и довольно хакерским способом.

Флаги, которые я передавал в pip, предназначались для уменьшения размера зависимостей c, а не зависимостей fortran.Поэтому не было никаких проблем с использованием предварительно скомпилированных зависимостей фортрана, которые обычно загружаются через pip.

Итак, сначала я создал эталонную версию неизмененного пакета scipy в папке sp:

pip install scipy -t sp

Затем я создал программу go, которая будет действовать как обертка вокруг компилятора gfortran (или технически вокруг ссылки на компилятор gfortran в / usr / bin)

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 на его место

Оболочка передаст большинство инструкций компилятору, но если инструкция состоит в том, чтобы скомпилировать программу на фортране, и скомпилированный двоичный файл уже существует вВ моей справочной версии scipy оболочка просто копирует справочную версию в новую компилируемую версию.

И это было сделано.Версии scipy и numpy уменьшенного размера теперь работают на aws lambda для python 3.6.

...