chore: import upstream snapshot with attribution
Build / Build and test on ubuntu-latest for x86_64 (push) Failing after 1s
Build / Build and test on ubuntu-latest (numpy 1.26) for x86_64 (push) Failing after 0s
Build / Build and test on ubuntu-24.04-arm for aarch64 (push) Has been cancelled
Build / Build and test on windows-11-arm for aarch64 (push) Has been cancelled
Build / Build and test on macos-latest for x86_64 (push) Has been cancelled
Build / Build and test on windows-latest for x86_64 (push) Has been cancelled
Build / Build and test on ubuntu-latest for x86_64 (push) Failing after 1s
Build / Build and test on ubuntu-latest (numpy 1.26) for x86_64 (push) Failing after 0s
Build / Build and test on ubuntu-24.04-arm for aarch64 (push) Has been cancelled
Build / Build and test on windows-11-arm for aarch64 (push) Has been cancelled
Build / Build and test on macos-latest for x86_64 (push) Has been cancelled
Build / Build and test on windows-latest for x86_64 (push) Has been cancelled
This commit is contained in:
+225
@@ -0,0 +1,225 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS = -n -v -v
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " applehelp to make an Apple Help Book"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " epub3 to make an epub3"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||
@echo " dummy to check syntax errors of document sources"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
.PHONY: html
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
.PHONY: dirhtml
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
.PHONY: singlehtml
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
.PHONY: pickle
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
.PHONY: json
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
.PHONY: htmlhelp
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
.PHONY: qthelp
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/numexpr3.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/numexpr3.qhc"
|
||||
|
||||
.PHONY: applehelp
|
||||
applehelp:
|
||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||
@echo
|
||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||
"~/Library/Documentation/Help or install it in your application" \
|
||||
"bundle."
|
||||
|
||||
.PHONY: devhelp
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/numexpr3"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/numexpr3"
|
||||
@echo "# devhelp"
|
||||
|
||||
.PHONY: epub
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
.PHONY: epub3
|
||||
epub3:
|
||||
$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
|
||||
@echo
|
||||
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
||||
|
||||
.PHONY: latex
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
.PHONY: latexpdf
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: latexpdfja
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: text
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
.PHONY: man
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
.PHONY: texinfo
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
.PHONY: info
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
.PHONY: gettext
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
.PHONY: changes
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
.PHONY: linkcheck
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
.PHONY: doctest
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||
@echo "Testing of coverage in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/coverage/python.txt."
|
||||
|
||||
.PHONY: xml
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
.PHONY: pseudoxml
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
|
||||
.PHONY: dummy
|
||||
dummy:
|
||||
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
|
||||
@echo
|
||||
@echo "Build finished. Dummy builder generates no files."
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
NumExpr API
|
||||
===========
|
||||
|
||||
.. automodule:: numexpr
|
||||
:members: evaluate, re_evaluate, disassemble, NumExpr, get_vml_version, set_vml_accuracy_mode, set_vml_num_threads, set_num_threads, detect_number_of_cores, detect_number_of_threads
|
||||
|
||||
.. py:attribute:: ncores
|
||||
|
||||
The number of (virtual) cores detected.
|
||||
|
||||
.. py:attribute:: nthreads
|
||||
|
||||
The number of threads currently in-use.
|
||||
|
||||
.. py:attribute:: MAX_THREADS
|
||||
|
||||
The maximum number of threads, as set by the environment variable ``NUMEXPR_MAX_THREADS``
|
||||
|
||||
.. py:attribute:: version
|
||||
|
||||
The version of NumExpr.
|
||||
|
||||
|
||||
Tests submodule
|
||||
---------------
|
||||
|
||||
.. automodule:: numexpr.tests
|
||||
:members: test, print_versions
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# numexpr documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sat Feb 4 17:19:36 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os, sys
|
||||
# import numexpr
|
||||
# module_location = os.path.dirname(numexpr.__file__)
|
||||
# sys.path.insert(0, os.path.abspath(module_location))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.imgmath',
|
||||
'sphinx.ext.ifconfig',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.githubpages',
|
||||
'numpydoc',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#
|
||||
source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'numexpr'
|
||||
copyright = '2006 (various)-2023, David M. Cooke, Francesc Alted, and others'
|
||||
author = 'David M. Cooke, Francesc Alted, and others'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.13'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.13.dev1'
|
||||
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'en'
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#
|
||||
# today = ''
|
||||
#
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents.
|
||||
# "<project> v<release> documentation" by default.
|
||||
#
|
||||
# html_title = 'numexpr va0'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (relative to this directory) to use as a favicon of
|
||||
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#
|
||||
# html_extra_path = []
|
||||
|
||||
# If not None, a 'Last updated on:' timestamp is inserted at every page
|
||||
# bottom, using the given strftime format.
|
||||
# The empty string is equivalent to '%b %d, %Y'.
|
||||
#
|
||||
# html_last_updated_fmt = None
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Language to be used for generating the HTML full-text search index.
|
||||
# Sphinx supports the following languages:
|
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
|
||||
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
|
||||
#
|
||||
# html_search_language = 'en'
|
||||
|
||||
# A dictionary with options for the search language support, empty by default.
|
||||
# 'ja' uses this config value.
|
||||
# 'zh' user can custom change `jieba` dictionary path.
|
||||
#
|
||||
# html_search_options = {'type': 'default'}
|
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
#
|
||||
# html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'numexprdoc'
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
#latex_documents = [
|
||||
# (master_doc, 'numexpr.tex', 'numexpr Documentation',
|
||||
# 'David M. Cooke, Francesc Alted, and others', 'manual'),
|
||||
#]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#
|
||||
# latex_appendices = []
|
||||
|
||||
# It false, will not define \strong, \code, itleref, \crossref ... but only
|
||||
# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
|
||||
# packages.
|
||||
#
|
||||
# latex_keep_old_macro_names = True
|
||||
|
||||
# If false, no module index is generated.
|
||||
#
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
#man_pages = [
|
||||
# (master_doc, 'numexpr', 'numexpr Documentation',
|
||||
# [author], 1)
|
||||
#]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
#texinfo_documents = [
|
||||
# (master_doc, 'numexpr', 'numexpr Documentation',
|
||||
# author, 'numexpr', 'One line description of project.',
|
||||
# 'Miscellaneous'),
|
||||
#]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
@@ -0,0 +1,27 @@
|
||||
.. numexpr documentation master file, created by
|
||||
sphinx-quickstart on Sat Feb 4 17:19:36 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
NumExpr Documentation Reference
|
||||
=================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
intro
|
||||
user_guide
|
||||
vm2
|
||||
mkl
|
||||
api
|
||||
release_notes
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -0,0 +1,95 @@
|
||||
How it works
|
||||
============
|
||||
|
||||
The string passed to :code:`evaluate` is compiled into an object representing the
|
||||
expression and types of the arrays used by the function :code:`numexpr`.
|
||||
|
||||
The expression is first compiled using Python's :code:`compile` function (this means
|
||||
that the expressions have to be valid Python expressions). From this, the
|
||||
variable names can be taken. The expression is then evaluated using instances
|
||||
of a special object that keep track of what is being done to them, and which
|
||||
builds up the parse tree of the expression.
|
||||
|
||||
This parse tree is then compiled to a bytecode program, which describes how to
|
||||
perform the operation element-wise. The virtual machine uses "vector registers":
|
||||
each register is many elements wide (by default 4096 elements). The key to
|
||||
NumExpr's speed is handling chunks of elements at a time.
|
||||
|
||||
There are two extremes to evaluating an expression elementwise. You can do each
|
||||
operation as arrays, returning temporary arrays. This is what you do when you
|
||||
use NumPy: :code:`2*a+3*b` uses three temporary arrays as large as :code:`a` or
|
||||
:code:`b`. This strategy wastes memory (a problem if your arrays are large),
|
||||
and also is not a good use of cache memory: for large arrays, the results of
|
||||
:code:`2*a` and :code:`3*b` won't be in cache when you do the add.
|
||||
|
||||
The other extreme is to loop over each element, as in::
|
||||
|
||||
for i in xrange(len(a)):
|
||||
c[i] = 2*a[i] + 3*b[i]
|
||||
|
||||
This doesn't consume extra memory, and is good for the cache, but, if the
|
||||
expression is not compiled to machine code, you will have a big case statement
|
||||
(or a bunch of if's) inside the loop, which adds a large overhead for each
|
||||
element, and will hurt the branch-prediction used on the CPU.
|
||||
|
||||
:code:`numexpr` uses a in-between approach. Arrays are handled as chunks (of
|
||||
4096 elements) at a time, using a register machine. As Python code,
|
||||
it looks something like this::
|
||||
|
||||
for i in xrange(0, len(a), 256):
|
||||
r0 = a[i:i+128]
|
||||
r1 = b[i:i+128]
|
||||
multiply(r0, 2, r2)
|
||||
multiply(r1, 3, r3)
|
||||
add(r2, r3, r2)
|
||||
c[i:i+128] = r2
|
||||
|
||||
(remember that the 3-arg form stores the result in the third argument,
|
||||
instead of allocating a new array). This achieves a good balance between
|
||||
cache and branch-prediction. And the virtual machine is written entirely in
|
||||
C, which makes it faster than the Python above. Furthermore the virtual machine
|
||||
is also multi-threaded, which allows for efficient parallelization of NumPy
|
||||
operations.
|
||||
|
||||
There is some more information and history at:
|
||||
|
||||
http://www.bitsofbits.com/2014/09/21/numpy-micro-optimization-and-numexpr/
|
||||
|
||||
Expected performance
|
||||
====================
|
||||
|
||||
The range of speed-ups for NumExpr respect to NumPy can vary from 0.95x and 20x,
|
||||
being 2x, 3x or 4x typical values, depending on the complexity of the
|
||||
expression and the internal optimization of the operators used. The strided and
|
||||
unaligned case has been optimized too, so if the expression contains such
|
||||
arrays, the speed-up can increase significantly. Of course, you will need to
|
||||
operate with large arrays (typically larger than the cache size of your CPU)
|
||||
to see these improvements in performance.
|
||||
|
||||
Here there are some real timings. For the contiguous case::
|
||||
|
||||
In [1]: import numpy as np
|
||||
In [2]: import numexpr as ne
|
||||
In [3]: a = np.random.rand(1e6)
|
||||
In [4]: b = np.random.rand(1e6)
|
||||
In [5]: timeit 2*a + 3*b
|
||||
10 loops, best of 3: 18.9 ms per loop
|
||||
In [6]: timeit ne.evaluate("2*a + 3*b")
|
||||
100 loops, best of 3: 5.83 ms per loop # 3.2x: medium speed-up (simple expr)
|
||||
In [7]: timeit 2*a + b**10
|
||||
10 loops, best of 3: 158 ms per loop
|
||||
In [8]: timeit ne.evaluate("2*a + b**10")
|
||||
100 loops, best of 3: 7.59 ms per loop # 20x: large speed-up due to optimised pow()
|
||||
|
||||
For unaligned arrays, the speed-ups can be even larger::
|
||||
|
||||
In [9]: a = np.empty(1e6, dtype="b1,f8")['f1']
|
||||
In [10]: b = np.empty(1e6, dtype="b1,f8")['f1']
|
||||
In [11]: a.flags.aligned, b.flags.aligned
|
||||
Out[11]: (False, False)
|
||||
In [12]: a[:] = np.random.rand(len(a))
|
||||
In [13]: b[:] = np.random.rand(len(b))
|
||||
In [14]: timeit 2*a + 3*b
|
||||
10 loops, best of 3: 29.5 ms per loop
|
||||
In [15]: timeit ne.evaluate("2*a + 3*b")
|
||||
100 loops, best of 3: 7.46 ms per loop # ~ 4x speed-up
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. epub3 to make an epub3
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
echo. coverage to run coverage check of the documentation if enabled
|
||||
echo. dummy to check syntax errors of document sources
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
REM Check if sphinx-build is available and fallback to Python version if any
|
||||
%SPHINXBUILD% 1>NUL 2>NUL
|
||||
if errorlevel 9009 goto sphinx_python
|
||||
goto sphinx_ok
|
||||
|
||||
:sphinx_python
|
||||
|
||||
set SPHINXBUILD=python -m sphinx.__init__
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:sphinx_ok
|
||||
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\numexpr3.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\numexpr3.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub3" (
|
||||
%SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "coverage" (
|
||||
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of coverage in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/coverage/python.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dummy" (
|
||||
%SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. Dummy builder generates no files.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
NumExpr with Intel MKL
|
||||
======================
|
||||
|
||||
Numexpr has support for Intel's VML (included in Intel's MKL) in order to
|
||||
accelerate the evaluation of transcendental functions on Intel CPUs. Here it
|
||||
is a small example on the kind of improvement you may get by using it.
|
||||
|
||||
A first benchmark
|
||||
-----------------
|
||||
|
||||
Firstly, we are going to exercise how MKL performs when computing a couple of
|
||||
simple expressions. One is a pure algebraic one: :code:`2*y + 4*x` and the other
|
||||
contains transcendental functions: :code:`sin(x)**2 + cos(y)**2`.
|
||||
|
||||
For this, we are going to use this worksheet_. I (Francesc Alted) ran this
|
||||
benchmark on a Intel Xeon E3-1245 v5 @ 3.50GHz. Here are the results when
|
||||
not using MKL::
|
||||
|
||||
NumPy version: 1.11.1
|
||||
Time for an algebraic expression: 0.168 s / 6.641 GB/s
|
||||
Time for a transcendental expression: 1.945 s / 0.575 GB/s
|
||||
Numexpr version: 2.6.1. Using MKL: False
|
||||
Time for an algebraic expression: 0.058 s / 19.116 GB/s
|
||||
Time for a transcendental expression: 0.283 s / 3.950 GB/s
|
||||
|
||||
|
||||
And now, using MKL::
|
||||
|
||||
NumPy version: 1.11.1
|
||||
Time for an algebraic expression: 0.169 s / 6.606 GB/s
|
||||
Time for a transcendental expression: 1.943 s / 0.575 GB/s
|
||||
Numexpr version: 2.6.1. Using MKL: True
|
||||
Time for an algebraic expression: 0.058 s / 19.153 GB/s
|
||||
Time for a transcendental expression: 0.075 s / 14.975 GB/s
|
||||
|
||||
|
||||
As you can see, numexpr using MKL can be up to 3.8x faster for the case of the
|
||||
transcendental expression. Also, you can notice that the pure algebraic
|
||||
expression is not accelerated at all. This is completely expected, as the
|
||||
MKL is offering accelerations for CPU bounded functions (sin, cos, tan, exp,
|
||||
log, sinh...) and not pure multiplications or adds.
|
||||
|
||||
Finally, note how numexpr+MKL can be up to 26x faster than using a pure NumPy
|
||||
solution. And this was using a processor with just four physical cores; you
|
||||
should expect more speedup as you throw more cores at that.
|
||||
|
||||
.. _worksheet: https://github.com/pydata/numexpr/blob/master/bench/vml_timing2.py
|
||||
|
||||
More benchmarks (older)
|
||||
-----------------------
|
||||
|
||||
Numexpr & VML can both use several threads for doing computations. Let's see
|
||||
how performance improves by using 1 or 2 threads on a 2-core Intel CPU (Core2
|
||||
E8400 @ 3.00GHz).
|
||||
|
||||
Using 1 thread
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Here we have some benchmarks on the improvement of speed that Intel's VML can
|
||||
achieve. First, look at times by some easy expression containing sine and
|
||||
cosine operations *without* using VML::
|
||||
|
||||
In [17]: ne.use_vml
|
||||
Out[17]: False
|
||||
|
||||
In [18]: x = np.linspace(-1, 1, 1e6)
|
||||
|
||||
In [19]: timeit np.sin(x)**2+np.cos(x)**2
|
||||
10 loops, best of 3: 43.1 ms per loop
|
||||
|
||||
In [20]: ne.set_num_threads(1)
|
||||
Out[20]: 2
|
||||
|
||||
In [21]: timeit ne.evaluate('sin(x)**2+cos(x)**2')
|
||||
10 loops, best of 3: 29.5 ms per loop
|
||||
|
||||
|
||||
and now using VML::
|
||||
|
||||
In [37]: ne.use_vml
|
||||
Out[37]: True
|
||||
|
||||
In [38]: x = np.linspace(-1, 1, 1e6)
|
||||
|
||||
In [39]: timeit np.sin(x)**2+np.cos(x)**2
|
||||
10 loops, best of 3: 42.8 ms per loop
|
||||
|
||||
In [40]: ne.set_num_threads(1)
|
||||
Out[40]: 2
|
||||
|
||||
In [41]: timeit ne.evaluate('sin(x)**2+cos(x)**2')
|
||||
100 loops, best of 3: 19.8 ms per loop
|
||||
|
||||
Hey, VML can accelerate computations by a 50% using a single CPU. That's great!
|
||||
|
||||
Using 2 threads
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
First, look at the time of the non-VML numexpr when using 2 threads::
|
||||
|
||||
In [22]: ne.set_num_threads(2)
|
||||
Out[22]: 1
|
||||
|
||||
In [23]: timeit ne.evaluate('sin(x)**2+cos(x)**2')
|
||||
100 loops, best of 3: 15.3 ms per loop
|
||||
|
||||
OK. We've got an almost perfect 2x improvement in speed with regard to the 1
|
||||
thread case. Let's see about the VML-powered numexpr version::
|
||||
|
||||
In [43]: ne.set_num_threads(2)
|
||||
Out[43]: 1
|
||||
|
||||
In [44]: timeit ne.evaluate('sin(x)**2+cos(x)**2')
|
||||
100 loops, best of 3: 12.2 ms per loop
|
||||
|
||||
Ok, that's about 1.6x improvement over the 1 thread VML computation, and
|
||||
still a 25% of improvement over the non-VML version. Good, native numexpr
|
||||
multithreading code really looks very efficient!
|
||||
|
||||
Numexpr native threading code vs VML's one
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You may already know that both numexpr and Intel's VML do have support for
|
||||
multithreaded computations, but you might be curious about which one is more
|
||||
efficient, so here it goes a hint. First, using the VML multithreaded
|
||||
implementation::
|
||||
|
||||
In [49]: ne.set_vml_num_threads(2)
|
||||
|
||||
In [50]: ne.set_num_threads(1)
|
||||
Out[50]: 1
|
||||
|
||||
In [51]: ne.set_vml_num_threads(2)
|
||||
|
||||
In [52]: timeit ne.evaluate('sin(x)**2+cos(x)**2')
|
||||
100 loops, best of 3: 16.8 ms per loop
|
||||
|
||||
and now, using the native numexpr threading code::
|
||||
|
||||
In [53]: ne.set_num_threads(2)
|
||||
Out[53]: 1
|
||||
|
||||
In [54]: ne.set_vml_num_threads(1)
|
||||
|
||||
In [55]: timeit ne.evaluate('sin(x)**2+cos(x)**2')
|
||||
100 loops, best of 3: 12 ms per loop
|
||||
|
||||
|
||||
This means that numexpr's native multithreaded code is about 40% faster than
|
||||
VML's for this case. So, in general, you should use the former with numexpr
|
||||
(and this is the default actually).
|
||||
|
||||
Mixing numexpr's and VML multithreading capabilities
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Finally, you might be tempted to use both multithreading codes at the same
|
||||
time, but you will be deceived about the improvement in performance::
|
||||
|
||||
In [57]: ne.set_vml_num_threads(2)
|
||||
|
||||
In [58]: timeit ne.evaluate('sin(x)**2+cos(x)**2')
|
||||
100 loops, best of 3: 17.7 ms per loop
|
||||
|
||||
Your code actually performs much worse. That's normal too because you are
|
||||
trying to run 4 threads on a 2-core CPU. For CPUs with many cores, you may
|
||||
want to try with different threading configurations, but as a rule of thumb,
|
||||
numexpr's one will generally win.
|
||||
@@ -0,0 +1,4 @@
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
.. include:: ../RELEASE_NOTES.rst
|
||||
@@ -0,0 +1,4 @@
|
||||
sphinx == 6.2.1
|
||||
numpy >= 1.13.3
|
||||
sphinx-rtd-theme == 1.2.2
|
||||
numpydoc == 1.5.0
|
||||
@@ -0,0 +1,340 @@
|
||||
NumExpr User Guide
|
||||
==================
|
||||
|
||||
The NumExpr package supplies routines for the fast evaluation of
|
||||
array expressions elementwise by using a vector-based virtual
|
||||
machine.
|
||||
|
||||
Using it is simple::
|
||||
|
||||
>>> import numpy as np
|
||||
>>> import numexpr as ne
|
||||
>>> a = np.arange(10)
|
||||
>>> b = np.arange(0, 20, 2)
|
||||
>>> c = ne.evaluate('2*a + 3*b')
|
||||
>>> c
|
||||
array([ 0, 8, 16, 24, 32, 40, 48, 56, 64, 72])
|
||||
|
||||
|
||||
It is also possible to use NumExpr to validate an expression::
|
||||
|
||||
>>> ne.validate('2*a + 3*b')
|
||||
|
||||
which returns `None` on success or raises an exception on invalid inputs.
|
||||
|
||||
and it can also re_evaluate an expression::
|
||||
|
||||
>>> b = np.arange(0, 40, 4)
|
||||
>>> ne.re_evaluate()
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
*NumExpr* requires Python_ 3.7 or greater, and NumPy_ 1.13 or greater. It is
|
||||
built in the standard Python way:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install .
|
||||
|
||||
You must have a C-compiler (i.e. MSVC Build tools on Windows and GCC on Linux) installed.
|
||||
|
||||
Then change to a directory that is not the repository directory (e.g. `/tmp`) and
|
||||
test :code:`numexpr` with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ python -c "import numexpr; numexpr.test()"
|
||||
|
||||
.. _Python: http://python.org
|
||||
.. _NumPy: http://numpy.scipy.org
|
||||
|
||||
|
||||
Enabling Intel VML support
|
||||
--------------------------
|
||||
|
||||
Starting from release 1.2 on, numexpr includes support for Intel's VML
|
||||
library. This allows for better performance on Intel architectures,
|
||||
mainly when evaluating transcendental functions (trigonometrical,
|
||||
exponential, ...). It also enables numexpr using several CPU cores.
|
||||
|
||||
If you have Intel's MKL (the library that embeds VML), just copy the
|
||||
:code:`site.cfg.example` that comes in the distribution to :code:`site.cfg` and
|
||||
edit the latter giving proper directions on how to find your MKL
|
||||
libraries in your system. After doing this, you can proceed with the
|
||||
usual building instructions listed above. Pay attention to the
|
||||
messages during the building process in order to know whether MKL has
|
||||
been detected or not. Finally, you can check the speed-ups on your
|
||||
machine by running the :code:`bench/vml_timing.py` script (you can play with
|
||||
different parameters to the :code:`set_vml_accuracy_mode()` and
|
||||
:code:`set_vml_num_threads()` functions in the script so as to see how it would
|
||||
affect performance).
|
||||
|
||||
Threadpool Configuration
|
||||
------------------------
|
||||
|
||||
Threads are spawned at import-time, with the number being set by the environment
|
||||
variable ``NUMEXPR_MAX_THREADS``. The default maximum thread count is **64**.
|
||||
There is no advantage to spawning more threads than the number of virtual cores
|
||||
available on the computing node. Practically NumExpr scales at large thread
|
||||
count (`> 8`) only on very large matrices (`> 2**22`). Spawning large numbers
|
||||
of threads is not free, and can increase import times for NumExpr or packages
|
||||
that import it such as Pandas or PyTables.
|
||||
|
||||
If desired, the number of threads in the pool used can be adjusted via an
|
||||
environment variable, ``NUMEXPR_NUM_THREADS`` (preferred) or ``OMP_NUM_THREADS``.
|
||||
Typically only setting ``NUMEXPR_MAX_THREADS`` is sufficient; the number of
|
||||
threads used can be adjusted dynamically via ``numexpr.set_num_threads(int)``.
|
||||
The number of threads can never exceed that set by ``NUMEXPR_MAX_THREADS``.
|
||||
|
||||
If the user has not configured the environment prior to importing NumExpr, info
|
||||
logs will be generated, and the initial number of threads *that are used*_ will
|
||||
be set to the number of cores detected in the system or 8, whichever is *less*.
|
||||
|
||||
Usage::
|
||||
|
||||
import os
|
||||
os.environ['NUMEXPR_MAX_THREADS'] = '16'
|
||||
os.environ['NUMEXPR_NUM_THREADS'] = '8'
|
||||
import numexpr as ne
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
`NumExpr`'s principal routine is::
|
||||
|
||||
evaluate(ex, local_dict=None, global_dict=None, optimization='aggressive', truediv='auto')
|
||||
|
||||
where :code:`ex` is a string forming an expression, like :code:`"2*a+3*b"`. The
|
||||
values for :code:`a` and :code:`b` will by default be taken from the calling
|
||||
function's frame (through the use of :code:`sys._getframe()`).
|
||||
Alternatively, they can be specified using the :code:`local_dict` or
|
||||
:code:`global_dict` arguments, or passed as keyword arguments.
|
||||
|
||||
The :code:`optimization` parameter can take the values :code:`'moderate'`
|
||||
or :code:`'aggressive'`. :code:`'moderate'` means that no optimization is made
|
||||
that can affect precision at all. :code:`'aggressive'` (the default) means that
|
||||
the expression can be rewritten in a way that precision *could* be affected, but
|
||||
normally very little. For example, in :code:`'aggressive'` mode, the
|
||||
transformation :code:`x~**3` -> :code:`x*x*x` is made, but not in
|
||||
:code:`'moderate'` mode.
|
||||
|
||||
The `truediv` parameter specifies whether the division is a 'floor division'
|
||||
(False) or a 'true division' (True). The default is the value of
|
||||
`__future__.division` in the interpreter. See PEP 238 for details.
|
||||
|
||||
Expressions are cached, so reuse is fast. Arrays or scalars are
|
||||
allowed for the variables, which must be of type 8-bit boolean (bool),
|
||||
32-bit signed integer (int), 64-bit signed integer (long),
|
||||
double-precision floating point number (float), 2x64-bit,
|
||||
double-precision complex number (complex) or raw string of bytes
|
||||
(str). If they are not in the previous set of types, they will be
|
||||
properly upcasted for internal use (the result will be affected as
|
||||
well). The arrays must all be the same size.
|
||||
|
||||
|
||||
Datatypes supported internally
|
||||
------------------------------
|
||||
|
||||
*NumExpr* operates internally only with the following types:
|
||||
|
||||
* 8-bit boolean (bool)
|
||||
* 32-bit signed integer (int or int32)
|
||||
* 64-bit signed integer (long or int64)
|
||||
* 32-bit single-precision floating point number (float or float32)
|
||||
* 64-bit, double-precision floating point number (double or float64)
|
||||
* 2x64-bit, double-precision complex number (complex or complex128)
|
||||
* Raw string of bytes (str in Python 2.7, bytes in Python 3+, numpy.str in both cases)
|
||||
|
||||
If the arrays in the expression does not match any of these types,
|
||||
they will be upcasted to one of the above types (following the usual
|
||||
type inference rules, see below). Have this in mind when doing
|
||||
estimations about the memory consumption during the computation of
|
||||
your expressions.
|
||||
|
||||
Also, the types in NumExpr conditions are somewhat stricter than those
|
||||
of Python. For instance, the only valid constants for booleans are
|
||||
:code:`True` and :code:`False`, and they are never automatically cast to integers.
|
||||
|
||||
|
||||
Casting rules
|
||||
-------------
|
||||
|
||||
Casting rules in NumExpr follow closely those of *NumPy*. However, for
|
||||
implementation reasons, there are some known exceptions to this rule,
|
||||
namely:
|
||||
|
||||
* When an array with type :code:`int8`, :code:`uint8`, :code:`int16` or
|
||||
:code:`uint16` is used inside NumExpr, it is internally upcasted to an
|
||||
:code:`int` (or :code:`int32` in NumPy notation).
|
||||
* When an array with type :code:`uint32` is used inside NumExpr, it is
|
||||
internally upcasted to a :code:`long` (or :code:`int64` in NumPy notation).
|
||||
* A floating point function (e.g. :code:`sin`) acting on :code:`int8` or
|
||||
:code:`int16` types returns a :code:`float64` type, instead of the
|
||||
:code:`float32` that is returned by NumPy functions. This is mainly due
|
||||
to the absence of native :code:`int8` or :code:`int16` types in NumExpr.
|
||||
* In operations implying a scalar and an array, the normal rules of casting
|
||||
are used in NumExpr, in contrast with NumPy, where array types takes
|
||||
priority. For example, if :code:`a` is an array of type :code:`float32`
|
||||
and :code:`b` is an scalar of type :code:`float64` (or Python :code:`float`
|
||||
type, which is equivalent), then :code:`a*b` returns a :code:`float64` in
|
||||
NumExpr, but a :code:`float32` in NumPy (i.e. array operands take priority
|
||||
in determining the result type). If you need to keep the result a
|
||||
:code:`float32`, be sure you use a :code:`float32` scalar too.
|
||||
|
||||
|
||||
Supported operators
|
||||
-------------------
|
||||
|
||||
*NumExpr* supports the set of operators listed below:
|
||||
|
||||
* Bitwise and logical operators (and, or, not, xor): :code:`&, |, ~, ^`
|
||||
* Comparison operators: :code:`<, <=, ==, !=, >=, >`
|
||||
* Unary arithmetic operators: :code:`-`
|
||||
* Binary arithmetic operators: :code:`+, -, *, /, //, **, %, <<, >>`
|
||||
|
||||
|
||||
Supported functions
|
||||
-------------------
|
||||
|
||||
The next are the current supported set:
|
||||
|
||||
* :code:`where(bool, number1, number2): number` -- number1 if the bool condition
|
||||
is true, number2 otherwise.
|
||||
* :code:`{isinf, isnan, isfinite}(float|complex): bool` -- returns element-wise True
|
||||
for ``inf`` or ``NaN``, ``NaN``, not ``inf`` respectively.
|
||||
* :code:`signbit(float|complex): bool` -- returns element-wise True if signbit is set
|
||||
False otherwise.
|
||||
* :code:`{sin,cos,tan}(float|complex): float|complex` -- trigonometric sine,
|
||||
cosine or tangent.
|
||||
* :code:`{arcsin,arccos,arctan}(float|complex): float|complex` -- trigonometric
|
||||
inverse sine, cosine or tangent.
|
||||
* :code:`arctan2(float1, float2): float` -- trigonometric inverse tangent of
|
||||
float1/float2.
|
||||
* :code:`hypot(float1, float2): float` -- Euclidean distance between float1, float2
|
||||
* :code:`nextafter(float1, float2): float` -- next representable floating-point value after
|
||||
float1 in direction of float2
|
||||
* :code:`copysign(float1, float2): float` -- return number with magnitude of float1 and
|
||||
sign of float2
|
||||
* :code:`{maximum,minimum}(float1, float2): float` -- return max/min of float1, float2
|
||||
* :code:`{sinh,cosh,tanh}(float|complex): float|complex` -- hyperbolic sine,
|
||||
cosine or tangent.
|
||||
* :code:`{arcsinh,arccosh,arctanh}(float|complex): float|complex` -- hyperbolic
|
||||
inverse sine, cosine or tangent.
|
||||
* :code:`{log,log10,log1p,log2}(float|complex): float|complex` -- natural, base-10 and
|
||||
log(1+x) logarithms.
|
||||
* :code:`{exp,expm1}(float|complex): float|complex` -- exponential and exponential
|
||||
minus one.
|
||||
* :code:`sqrt(float|complex): float|complex` -- square root.
|
||||
* :code:`trunc(float): float` -- round towards zero
|
||||
* :code:`round(float|complex|int): float|complex|int` -- round to nearest integer (`rint`)
|
||||
* :code:`sign(float|complex|int): float|complex|int` -- return -1, 0, +1 depending on sign
|
||||
* :code:`abs(float|complex|int): float|complex|int` -- absolute value.
|
||||
* :code:`conj(complex): complex` -- conjugate value.
|
||||
* :code:`{real,imag}(complex): float` -- real or imaginary part of complex.
|
||||
* :code:`complex(float, float): complex` -- complex from real and imaginary
|
||||
parts.
|
||||
* :code:`contains(np.str, np.str): bool` -- returns True for every string in :code:`op1` that
|
||||
contains :code:`op2`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
* :code:`abs()` for complex inputs returns a :code:`complex` output too. This is a
|
||||
departure from NumPy where a :code:`float` is returned instead. However,
|
||||
NumExpr is not flexible enough yet so as to allow this to happen.
|
||||
Meanwhile, if you want to mimic NumPy behaviour, you may want to select the
|
||||
real part via the :code:`real` function (e.g. :code:`real(abs(cplx))`) or via the
|
||||
:code:`real` selector (e.g. :code:`abs(cplx).real`).
|
||||
|
||||
More functions can be added if you need them. Note however that NumExpr 2.6 is
|
||||
in maintenance mode and a new major revision is under development.
|
||||
|
||||
Supported reduction operations
|
||||
------------------------------
|
||||
|
||||
The next are the current supported set:
|
||||
|
||||
* :code:`sum(number, axis=None)`: Sum of array elements over a given axis.
|
||||
Negative axis are not supported.
|
||||
* :code:`prod(number, axis=None)`: Product of array elements over a given axis.
|
||||
Negative axis are not supported.
|
||||
|
||||
*Note:* because of internal limitations, reduction operations must appear the
|
||||
last in the stack. If not, it will be issued an error like::
|
||||
|
||||
>>> ne.evaluate('sum(1)*(-1)')
|
||||
RuntimeError: invalid program: reduction operations must occur last
|
||||
|
||||
General routines
|
||||
----------------
|
||||
|
||||
* :code:`evaluate(expression, local_dict=None, global_dict=None,
|
||||
optimization='aggressive', truediv='auto')`: Evaluate a simple array
|
||||
expression element-wise. See examples above.
|
||||
* :code:`re_evaluate(local_dict=None)`: Re-evaluate the last array expression
|
||||
without any check. This is meant for accelerating loops that are re-evaluating
|
||||
the same expression repeatedly without changing anything else than the operands.
|
||||
If unsure, use evaluate() which is safer.
|
||||
* :code:`test()`: Run all the tests in the test suite.
|
||||
* :code:`print_versions()`: Print the versions of software that numexpr relies on.
|
||||
* :code:`set_num_threads(nthreads)`: Sets a number of threads to be used in operations.
|
||||
Returns the previous setting for the number of threads. See note below to see
|
||||
how the number of threads is set via environment variables.
|
||||
|
||||
If you are using VML, you may want to use *set_vml_num_threads(nthreads)* to
|
||||
perform the parallel job with VML instead. However, you should get very
|
||||
similar performance with VML-optimized functions, and VML's parallelizer
|
||||
cannot deal with common expressions like `(x+1)*(x-2)`, while NumExpr's
|
||||
one can.
|
||||
|
||||
* :code:`detect_number_of_cores()`: Detects the number of cores on a system.
|
||||
|
||||
|
||||
Intel's VML specific support routines
|
||||
-------------------------------------
|
||||
|
||||
When compiled with Intel's VML (Vector Math Library), you will be able
|
||||
to use some additional functions for controlling its use. These are:
|
||||
|
||||
* :code:`set_vml_accuracy_mode(mode)`: Set the accuracy for VML operations.
|
||||
|
||||
The :code:`mode` parameter can take the values:
|
||||
|
||||
- :code:`'low'`: Equivalent to VML_LA - low accuracy VML functions are called
|
||||
- :code:`'high'`: Equivalent to VML_HA - high accuracy VML functions are called
|
||||
- :code:`'fast'`: Equivalent to VML_EP - enhanced performance VML functions are called
|
||||
|
||||
It returns the previous mode.
|
||||
|
||||
This call is equivalent to the :code:`vmlSetMode()` in the VML library. See:
|
||||
|
||||
http://www.intel.com/software/products/mkl/docs/webhelp/vml/vml_DataTypesAccuracyModes.html
|
||||
|
||||
for more info on the accuracy modes.
|
||||
|
||||
* :code:`set_vml_num_threads(nthreads)`: Suggests a maximum number of
|
||||
threads to be used in VML operations.
|
||||
|
||||
This function is equivalent to the call
|
||||
:code:`mkl_domain_set_num_threads(nthreads, MKL_VML)` in the MKL library.
|
||||
See:
|
||||
|
||||
http://www.intel.com/software/products/mkl/docs/webhelp/support/functn_mkl_domain_set_num_threads.html
|
||||
|
||||
for more info about it.
|
||||
|
||||
* :code:`get_vml_version()`: Get the VML/MKL library version.
|
||||
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
.. include:: ../AUTHORS.txt
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
NumExpr is distributed under the MIT_ license.
|
||||
|
||||
.. _MIT: http://www.opensource.org/licenses/mit-license.php
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
Performance of the Virtual Machine in NumExpr2.0
|
||||
================================================
|
||||
|
||||
Numexpr 2.0 leverages a new virtual machine completely based on the new ndarray
|
||||
iterator introduced in NumPy 1.6. This represents a nice combination of the
|
||||
advantages of using the new iterator, while retaining the ability to avoid
|
||||
copies in memory as well as the multi-threading capabilities of the previous
|
||||
virtual machine (1.x series).
|
||||
|
||||
The increased performance of the new virtual machine can be seen in several
|
||||
scenarios, like:
|
||||
|
||||
* *Broadcasting*. Expressions containing arrays that needs to be broadcasted,
|
||||
will not need additional memory (i.e. they will be broadcasted on-the-fly).
|
||||
* *Non-native dtypes*. These will be translated to native dtypes on-the-fly,
|
||||
so there is not need to convert the whole arrays first.
|
||||
* *Fortran-ordered arrays*. The new iterator will find the best path to
|
||||
optimize operations on such arrays, without the need to transpose them first.
|
||||
|
||||
There is a drawback though: performance with small arrays suffers a bit because
|
||||
of higher set-up times for the new virtual machine. See below for detailed
|
||||
benchmarks.
|
||||
|
||||
Some benchmarks for best-case scenarios
|
||||
---------------------------------------
|
||||
|
||||
Here you have some benchmarks of some scenarios where the new virtual machine
|
||||
actually represents an advantage in terms of speed (also memory, but this is
|
||||
not shown here). As you will see, the improvement is notable in many areas,
|
||||
ranging from 3x to 6x faster operations.
|
||||
|
||||
Broadcasting
|
||||
^^^^^^^^^^^^
|
||||
|
||||
>>> a = np.arange(1e3)
|
||||
>>> b = np.arange(1e6).reshape(1e3, 1e3)
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 1.4.2
|
||||
100 loops, best of 3: 16.4 ms per loop
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 2.0
|
||||
100 loops, best of 3: 5.2 ms per loop
|
||||
|
||||
|
||||
Non-native types
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
>>> a = np.arange(1e6, dtype=">f8")
|
||||
>>> b = np.arange(1e6, dtype=">f8")
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 1.4.2
|
||||
100 loops, best of 3: 17.2 ms per loop
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 2.0
|
||||
100 loops, best of 3: 6.32 ms per loop
|
||||
|
||||
|
||||
Fortran-ordered arrays
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
>>> a = np.arange(1e6).reshape(1e3, 1e3).copy('F')
|
||||
>>> b = np.arange(1e6).reshape(1e3, 1e3).copy('F')
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 1.4.2
|
||||
10 loops, best of 3: 32.8 ms per loop
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 2.0
|
||||
100 loops, best of 3: 5.62 ms per loop
|
||||
|
||||
|
||||
|
||||
Mix of 'non-native' arrays, Fortran-ordered, and using broadcasting
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
>>> a = np.arange(1e3, dtype='>f8').copy('F')
|
||||
>>> b = np.arange(1e6, dtype='>f8').reshape(1e3, 1e3).copy('F')
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 1.4.2
|
||||
10 loops, best of 3: 21.2 ms per loop
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 2.0
|
||||
100 loops, best of 3: 5.22 ms per loop
|
||||
|
||||
|
||||
Longer setup-time
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The only drawback of the new virtual machine is during the computation of
|
||||
small arrays::
|
||||
|
||||
>>> a = np.arange(10)
|
||||
>>> b = np.arange(10)
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 1.4.2
|
||||
10000 loops, best of 3: 22.1 µs per loop
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 2.0
|
||||
10000 loops, best of 3: 30.6 µs per loop
|
||||
|
||||
|
||||
i.e. the new virtual machine takes a bit more time to set-up (around 8 µs in
|
||||
this machine). However, this should be not too important because for such a
|
||||
small arrays NumPy is always a better option::
|
||||
|
||||
>>> timeit c = a*(b+1)
|
||||
100000 loops, best of 3: 4.16 µs per loop
|
||||
|
||||
|
||||
And for arrays large enough the difference is negligible::
|
||||
|
||||
>>> a = np.arange(1e6)
|
||||
>>> b = np.arange(1e6)
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 1.4.2
|
||||
100 loops, best of 3: 5.77 ms per loop
|
||||
|
||||
>>> timeit ne.evaluate("a*(b+1)") # 2.0
|
||||
100 loops, best of 3: 5.77 ms per loop
|
||||
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
The new virtual machine introduced in numexpr 2.0 brings more performance in
|
||||
many different scenarios (broadcast, non-native dtypes, Fortran-orderd arrays),
|
||||
while it shows slightly worse performance for small arrays. However, as
|
||||
numexpr is more geared to compute large arrays, the new virtual machine should
|
||||
be good news for numexpr users in general.
|
||||
Reference in New Issue
Block a user