Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack CPU Profiles #5

Open
JonWallsten opened this issue Jan 27, 2021 · 43 comments
Open

Webpack CPU Profiles #5

JonWallsten opened this issue Jan 27, 2021 · 43 comments

Comments

@JonWallsten
Copy link
Owner

JonWallsten commented Jan 27, 2021

Single production build

Webpack 5

Versions
webpack: 5.18.0
webpack-cli: 4.4.0
node: 14.15.4
windows: 10.1809

Script
node --max_old_space_size=8192 ../../node_modules/webpack/bin/webpack.js "--config" "config/webpack.prod.ts"

Config

// webpack.base.ts
import { Configuration, DefinePlugin } from 'webpack';
import * as helpers from './helpers';
import { projectRootPath } from '../../../build-tools/helpers';

export default (options: any) => {
    const isProd: boolean = options.env === 'production';

    const config: Configuration = {
        output: {
            path: helpers.rootPath('dist'),
            filename: '[name].js',
            libraryTarget: 'umd',
            globalObject: 'this'
        },
        entry: {
            'index': './src/index.ts'
        },
        resolve: {
            extensions: ['.ts', '.js'],
            modules: [
                helpers.rootPath('src'),
                helpers.rootPath('node_modules'),
                projectRootPath('node_modules')
            ],
            fallback: {
                'https': require.resolve('https-browserify'),
                'http': false,
                'process': require.resolve('process/browser')
            }
        },
        externals: /^(lodash|axios)$/i,
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    loader: 'lodash-ts-imports-loader',
                    include: helpers.include,
                    enforce: 'pre'
                }
            ]
        },
        plugins: [
            new DefinePlugin({
                IS_PROD: isProd,
                IS_DEV: !isProd
            }),
        ]
    };
    return config;
};
// webpack.prod.ts
import webpackMerge from 'webpack-merge';
import * as webpack from 'webpack';
import * as helpers from './helpers';
import commonConfig from './webpack.base';

import * as TerserPlugin from 'terser-webpack-plugin';

export default () => {
    const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
    const useSourcemaps: boolean = process.env.USE_SOURCEMAPS !== undefined;

    return webpackMerge(commonConfig({ env: ENV }), {
        mode: ENV,
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    loader: 'ts-loader',
                    options: {
                        configFile: '../tsconfig.build.json'
                    },
                    include: helpers.include
                }
            ]
        },

        devtool: useSourcemaps ? 'inline-source-map' : false,

        optimization: {
            minimizer: [
                new webpack.debug.ProfilingPlugin(),

                new TerserPlugin({
                    terserOptions: {
                        ecma: 5,
                        keep_classnames: true,
                        keep_fnames: true,
                        sourceMap: useSourcemaps && {
                            url: 'inline'
                        },
                        mangle: {
                            reserved: [ 'process' ] // Preserve process.env in dist (needed for config based on runtime env in node scripts)
                        }
                    }
                })
            ]
        }
    });
};

Profile
events_webpack_5.18.0.zip

Webpack 4

Versions
webpack: 4.44.1
webpack-cli: 3.3.12
node: 14.15.4
windows: 10.1809

Script:
node --max_old_space_size=8192 ../../node_modules/webpack/bin/webpack.js "--config" "config/webpack.prod.ts" "--display" "errors-only" "--display-error-details"

Config

// webpack.base.ts
import { DefinePlugin } from 'webpack';
import * as helpers from './helpers';
import { projectRootPath } from '../../../build-tools/helpers';

export default (options: any) => {
    const isProd: boolean = options.env === 'production';

    const config: any = {
        output: {
            path: helpers.rootPath('dist'),
            filename: '[name].js',
            libraryTarget: 'umd',
            globalObject: 'this'
        },
        entry: {
            'index': './src/index.ts'
        },
        resolve: {
            extensions: ['.ts', '.js'],
            modules: [
                helpers.rootPath('src'),
                helpers.rootPath('node_modules'),
                projectRootPath('node_modules')
            ]
        },
        externals: /^(lodash|axios)$/i,
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    loader: 'lodash-ts-imports-loader',
                    include: helpers.include,
                    enforce: 'pre'
                }
            ]
        },

        plugins: [
            new DefinePlugin({
                IS_PROD: isProd,
                IS_DEV: !isProd
            }),
        ],
        node: {
            global: true,
            crypto: 'empty',
            process: true,
            module: false,
            clearImmediate: false,
            setImmediate: false
        }
    };

    return config;
};
// webpack.prod.ts
import webpackMerge from 'webpack-merge';
import * as webpack from 'webpack';
import * as helpers from './helpers';
import commonConfig from './webpack.base';

import * as TerserPlugin from 'terser-webpack-plugin';

export default () => {
    const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
    const useSourcemaps: boolean = process.env.USE_SOURCEMAPS !== undefined;

    return webpackMerge(commonConfig({ env: ENV }), {
        mode: ENV,
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    loader: 'ts-loader',
                    options: {
                        configFile: '../tsconfig.build.json'
                    },
                    include: helpers.include
                }
            ]
        },
        infrastructureLogging: {
            level: 'verbose'
        },

        devtool: useSourcemaps ? 'inline-source-map' : false,

        optimization: {
            minimizer: [
                new webpack.debug.ProfilingPlugin(),

                new TerserPlugin({
                    terserOptions: {
                        ecma: 5,
                        keep_classnames: true,
                        keep_fnames: true,
                        sourceMap: useSourcemaps && {
                            url: 'inline'
                        },
                        mangle: {
                            reserved: [ 'process' ] // Preserve process.env in dist (needed for config based on runtime env in node scripts)
                        }
                    }
                })
            ]
        }
    });
};

Profile
events_webpack_4.44.1.zip

More info:
webpack/webpack#12475

@JonWallsten
Copy link
Owner Author

@alexander-akait: Added config and versions. Do you need anything else?

@alexander-akait
Copy link
Collaborator

Which branch should I use? And here we already working webpack/webpack#12516 😄

@JonWallsten
Copy link
Owner Author

JonWallsten commented Jan 27, 2021

Sorry, I don't have repro for this since it requires publishing the complete source code on a third party service. Not sure I'm allowed to do that to be honest. I could maybe do a dumbed down version, but that would have so little code that it would build in an instant anyway.

@alexander-akait
Copy link
Collaborator

Thanks for profiles, can you try to disable lodash-ts-imports-loader?

@JonWallsten
Copy link
Owner Author

I've done that already. I disabled all unnecessary stuff when I measured the build times in webpack/webpack#12475.
It has no major impact of the build time. I can do new profiles without it of you need it.

@alexander-akait
Copy link
Collaborator

No need, thanks

@JonWallsten
Copy link
Owner Author

Total build time for all our libs and apps are up from ~2min 20s -> ~3min 10s

@alexander-akait
Copy link
Collaborator

ts-loader is very slow, do you need check types, maybe you can move it from build on lint task before build?

@alexander-akait
Copy link
Collaborator

Try to disable check types and look how it reduce build time

@JonWallsten
Copy link
Owner Author

@alexander-akait I can play around with that tomorrow. But does that explain the difference between 4 and 5? I do TypeCheck in both.

@alexander-akait
Copy link
Collaborator

webpack@5 do more stuff, unfortunately it's hard to say what exactly is regression without code, maybe you have some edge cases

@JonWallsten
Copy link
Owner Author

Ah, I see. Yeah, creating a repro would probably remove the "bug". And showing the complete codebase will probably not be possible or make sense to you. Hopefully enough people can post CPU profiles until you find a pattern. We have upgraded now anyway, but I hope the build times goes down in the future. Thanks for looking.

@alexander-akait
Copy link
Collaborator

@JonWallsten webpack/webpack#12557 can help to you in future, just want to clarify cache is working?

@JonWallsten
Copy link
Owner Author

JonWallsten commented Feb 2, 2021

@alexander-akait I had some issue yesterday which might have something to do with the cache. It complained about some hash thingy in a file called crypto something. I didn't save the log though.

Haha, I just checked the terminal to see if I still had the error, and luckily enough the same issue just happened and crashed by build process.

Probably happened when I switched branch in git. Might be unrelated though, but have never seen the message before turning on cache.

TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received 
undefined
 |     at Hash.update packages\web-app-edit\(internal\crypto\hash.js:84:11) 
 |     at BulkUpdateDecorator.update (C:\Users\***\repo\oas-web\node_modules\webpack\lib\util\createHash.js:51:14)
 |     at NormalModule.updateHash (C:\Users\***\repo\oas-web\node_modules\webpack\lib\NormalModule.js:1115:8)
 |     at Compilation._createModuleHash (C:\Users\***\repo\oas-web\node_modules\webpack\lib\Compilation.js:2906:10)
 |     at Compilation.createModuleHashes (C:\Users\***\repo\oas-web\node_modules\webpack\lib\Compilation.js:2878:10)
 |     at C:\Users\***\repo\oas-web\node_modules\webpack\lib\Compilation.js:2155:11
 |     at Hook.eval [as callAsync] (eval at create (C:\Users\***\repo\oas-web\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:6:1)
 |     at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:\Users\***\repo\oas-web\node_modules\tapable\lib\Hook.js:18:14)
 |     at C:\Users\***\repo\oas-web\node_modules\webpack\lib\Compilation.js:2115:36
 |     at Hook.eval [as callAsync] (eval at create (C:\Users\jway52\repo\oas-web\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:6:1)

@alexander-akait
Copy link
Collaborator

Interesting... Can you try to add console.log to webpack\lib\util\createHash? We need to check why data is not Buffer or something else

@JonWallsten
Copy link
Owner Author

Sure! It might take a day or so to reproduce though. I'll just wait until it happens again.

@JonWallsten
Copy link
Owner Author

JonWallsten commented Feb 2, 2021

What exactly do we need to find out? I just noticed the log says data is undefined. So that would not be helpful to log. Should I log somewhere earlier in the stack? Maybe source file, or module name or something would be better?

@alexander-akait
Copy link
Collaborator

Maybe hook where it happens, I think you can get it here oas-web\node_modules\tapable\lib\HookCodeFactory (from stack)

@JonWallsten
Copy link
Owner Author

Maybe hook where it happens, I think you can get it here oas-web\node_modules\tapable\lib\HookCodeFactory (from stack)

I'll try! I'll log out whenever data is undefined and see what we get on the hook! pun intended

@JonWallsten
Copy link
Owner Author

@alexander-akait
Got another error:
I switched branch to a branch we're I have renamed a scss file while dev-server was running, but idle.

<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'packages\web-app-edit\src\app\components\term-dialog\term-dialog.component.scss|0|Compilation\modules|C:\Users\jway52\repo\oas-web\node_modules\css-loader\dist\cjs.js?esModule=false!C:\Users\jway52\repo\oas-web\node_modules\postcss-loader\dist\cjs.js!C:\Users\jway52\repo\oas-web\node_modules\sass-loader\dist\cjs.js!C:\Users\jway52\repo\oas-web\packages\web-app-edit\src\app\components\term-dialog\term-dialog.component.scss': No serializer registered for SassError
 | <w> while serializing packages\web-app-edit\webpack\lib\cache\PackFileCacheStrategy.PackContentItems -> webpack/lib/NormalModule -> webpack/lib/ModuleBuildError -> SassError

@JonWallsten
Copy link
Owner Author

Logs are in place for the other issue.

@alexander-akait
Copy link
Collaborator

alexander-akait commented Feb 3, 2021

<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'packages\web-app-edit\src\app\components\term-dialog\term-dialog.component.scss|0|Compilation\modules|C:\Users\jway52\repo\oas-web\node_modules\css-loader\dist\cjs.js?esModule=false!C:\Users\jway52\repo\oas-web\node_modules\postcss-loader\dist\cjs.js!C:\Users\jway52\repo\oas-web\node_modules\sass-loader\dist\cjs.js!C:\Users\jway52\repo\oas-web\packages\web-app-edit\src\app\components\term-dialog\term-dialog.component.scss': No serializer registered for SassError

You have error in sass files (wrong syntax, something was not resolved), so module can be cached, what is stats value? You should get error message and stack for this

@JonWallsten
Copy link
Owner Author

Yeah, it's probably because I renamed a file which is then imported in a bunch of different files, so they probably temporarily had the wrong path. Maybe the error message could be made a little bit nicer, it looks like something is seriously wrong. But it will obviously fix itself after the next rebuilds when the branch switch is stable.

@alexander-akait
Copy link
Collaborator

alexander-akait commented Feb 3, 2021

@JonWallsten You have something wrong with your output (what is the value for stats?), webpack already print detailed error(s)

@JonWallsten
Copy link
Owner Author

Let me try to reproduce the issue. Hold on.

@JonWallsten
Copy link
Owner Author

We're also seing this for half the team but not the rest of it:

 |
 | [webpack-cli] TypeError: cli.isValidationError is not a function
 |     at Command.<anonymous> (c:\OAS\gitlab\oas-web\node_modules\@webpack-cli\serve\lib\index.js:96:25)

Can't find any issue or Stackoverflow for it. I've asked them to clear node_modules and install everything again, but the error persists. Any idea?

@alexander-akait
Copy link
Collaborator

You have old version of webpack-cli, please install latest version of webpack-cli and @webpack-cli/serve

@JonWallsten
Copy link
Owner Author

Hmm, that's the thing, we all have the same package.json so we all have webpack-cli@4.4.0. Maybe the version of @webpack-cli/serve has changed or something. I'll bump to the latest of everything and make them install again.

@alexander-akait
Copy link
Collaborator

Please install webpack-cli@4.5.0, you update webpack-cli/serve, but doesn't update webpack-cli

@JonWallsten
Copy link
Owner Author

I'll do! Thanks!

@JonWallsten
Copy link
Owner Author

JonWallsten commented Feb 9, 2021

@alexander-akait: Found some more cache issues after switching branch and then rebase on master.

 ERROR in packages\web-lib-core\.\src\pojo\apiresult.to.pojo.ts
 | [tsl] ERROR 
 |       TS18002: The 'files' list in config file 'tsconfig.json' is empty.
 |  @ packages\web-lib-core\src\oasapi2\oasapi2.class.ts 23:26-62
 |  @ packages\web-lib-core\src\index.ts 57:21-55
 |
 | ERROR in packages\web-lib-core\.\src\pojo\apiresult.to.pojo.ts 
 | Module build failed (from node_modules\ts-loader\index.js):
 | Error: error while parsing tsconfig.json
 |     at Object.loader (C:\Users\jway52\repo\oas-web\node_modules\ts-loader\dist\index.js:18:18)
 |  @ packages\web-lib-core\src\oasapi2\oasapi2.class.ts 23:26-62 
 |  @ packages\web-lib-core\src\index.ts 57:21-55

I had to clean the cache folder for it to work again.

For reference this is my 'files' list:
image

Normally I clean the cache between each cold start. But wanted to test if we could save time since I restart from a few times every day due to various issue.

@alexander-akait
Copy link
Collaborator

alexander-akait commented Feb 9, 2021

@JonWallsten You need to open an issue in ts-loader repo

@JonWallsten
Copy link
Owner Author

JonWallsten commented Feb 12, 2021

@alexander-akait: Quick question, answer if you have time, would really appreciate it! :)

We're using Webpack to build our monorepo. We have a few libraries; core, common and angular, and a few applications. We have libraryTarget: 'umd' for all of our libraries. Is tree shaking not possible when consuming an umd library? I have the feeling that we include the complete lib and not just what we're using. If this is not possible, would it be possible with the new ESModules? webpack/webpack#2933

Thank you and have a great weekend!

@alexander-akait
Copy link
Collaborator

If this is not possible, would it be possible with the new ESModules?

Yes, but we are working on it right now

@JonWallsten
Copy link
Owner Author

If this is not possible, would it be possible with the new ESModules?

Yes, but we are working on it right now

Perfect! Then we won't do anything until that's done!
Keep up the good work! :)

@alexander-akait
Copy link
Collaborator

@JonWallsten But to be honestly recommend do not bundle library and keep them in ES modules format, it will be faster and more advanced and simple

@JonWallsten
Copy link
Owner Author

JonWallsten commented Feb 12, 2021

@alexander-akait I trust you. We are discussing about skipping bundling, it's not really necessary, or keep both the source files and a bundle for compobility. Is that possible in Webpack today? Do you have any example lib that uses Webpack that does this? Thanks!

A note to this is that we're using TypeScript, so everything is a bit more complicated with sourcemaps, type declaration and type declaration maps.

@alexander-akait
Copy link
Collaborator

You don't need webpack here, just write ES module syntax code 🛩️ If you use ts, you can setup it in tsconfig.json

@JonWallsten
Copy link
Owner Author

Ah, that's true. The only downside I can think of is the handling on resources (images/css/fonts) etc. But we can probably solve that somehow.

@alexander-akait
Copy link
Collaborator

images and fonts can be solved using new URL('./image.png', import.meta.url);, it will work in Node.js and browsers, for CSS it is more difficult but we are thinking about it (the new major release of css-loader will use new URL syntax for a { background: url(); }), so if you will not have @import in your CSS files, all will work in node and browser too

@JonWallsten
Copy link
Owner Author

I double checked, we don't have any resources that I could find. So that's not an issue.
But we are using some Webpack plugins/features that I'm not sure how we would replace:

import { Configuration, DefinePlugin } from 'webpack';
import * as helpers from './helpers';
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

/**
 * Webpack Plugins
 */
import { projectRootPath } from '../../../build-tools/helpers';

export interface IWebpackOptions {
    env?: string;
}

/**
 * Webpack configuration
 *
 * See: http://webpack.github.io/docs/configuration.html#cli
 */
export default (options: IWebpackOptions) => {
    const isProd: boolean = options.env === 'production';

    const config: Configuration = {
        /**
         * CAUTION:
         * web-app-wui must be built using legacy ES5, e.g. no arrow functions and classes (ES6+),
         * since we still need to support IE11 for Catia/Enovia (MMT1-29082)
         */
        target: ['web', 'es5'],
        output: {
            path: helpers.rootPath('dist'),
            filename: '[name].js',
            libraryTarget: 'umd',
            globalObject: 'this'
        },
        /**
         * The entry point for the bundle
         * Our Angular.js app
         *
         * See: http://webpack.github.io/docs/configuration.html#entry
         */
        entry: {
            'index': './src/index.ts'
        },

        /**
         * Options affecting the resolving of modules.
         *
         * See: http://webpack.github.io/docs/configuration.html#resolve
         */
        resolve: {
            /**
             * An array of extensions that should be used to resolve modules.
             *
             * See: http://webpack.github.io/docs/configuration.html#resolve-extensions
             */
            extensions: ['.ts', '.js'],

            /**
             * An array of directory names to be resolved to the current directory
             */
            modules: [
                helpers.rootPath('src'),
                helpers.rootPath('node_modules'),
                projectRootPath('node_modules')
            ],
            fallback: {
                'https': require.resolve('https-browserify'),
                'http': false,
                'process': require.resolve('process/browser')
            }
        },
        // When using external packages
        externals: /^(lodash|axios)$/i,

        /**
         * Options affecting the normal modules.
         *
         * See: http://webpack.github.io/docs/configuration.html#module
         */
        module: {

            rules: [
                /**
                 * Optimises lodash import to allow syntax "import { name } from 'lodash'" instead of
                 * import * as name from 'lodash/name'
                 *
                 */
                {
                    test: /\.ts$/,
                    loader: 'lodash-ts-imports-loader',
                    include: helpers.include,
                    enforce: 'pre'
                }
            ]

        },

        /**
         * Add additional plugins to the compiler.
         *
         * See: http://webpack.github.io/docs/configuration.html#plugins
         */
        plugins: [
            /**
             * Plugin: DefinePlugin
             * Description: Define free variables.
             * Useful for having development builds with debug logging or adding global constants.
             *
             * Environment helpers
             *
             * See: https://webpack.js.org/plugins/define-plugin/#root
             */
            // NOTE: when adding more properties make sure you include them in typings/global.d.ts
            new DefinePlugin({
                IS_PROD: isProd,
                IS_DEV: !isProd,
                DEV_SERVER_URL: JSON.stringify('https://l4124t.sss.se.scania.com/oas/utils/')
            }),

            new ForkTsCheckerWebpackPlugin()
        ]
    };

    return config;
};

The lodash thing is not really important. But DefinePlugin and fallback are things we are using.

@alexander-akait
Copy link
Collaborator

Hm, I see, let's wait the module type for libraries

@JonWallsten
Copy link
Owner Author

JonWallsten commented Feb 12, 2021

Thanks for your time! We'll investigate in our team :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants