In Intel® IPP 8.2 and later versions, multi-threading (internal threading) libraries are deprecated due to issues with performance and interoperability with other threading models, but made available for legacy applications. Multi-threaded static and dynamic libraries are available as a separate download to support legacy applications. For new applications development, highly recommended to use the single-threaded versions with application-level threading (as shown in the below picture).
Intel® IPP 8.2 and later versions installation will have single threaded libraries in the following directory Structure
<ipp directory>lib/ia32– Single-threaded Static and Dynamic for IA32 architecture
<ipp directory>lib/intel64- Single-threaded Static and Dynamic for Intel 64 architecture
Static linking (Both single threaded and Multi-threaded libraries)
Windows* OS: mt suffix in a library name (ipp<domain>mt.lib)
Linux* OS and OS X*: no suffix in a library name (libipp<domain>.a)
Dynamic Linking: Default (no suffix)
Windows* OS: ipp<domain>.lib
Linux* OS: libipp<domain>.a
OS X*: libipp<domain>.dylib
Q: Does Intel® IPP supports external multi-threading? Thread safe?
Ans: Yes, Intel® IPP supports external threading as in the below picture. User has option to use different threading models like Intel TBB, Intel Cilk Plus, Windows * threads, OpenMP or PoSIX. All Intel® Integrated Performance Primitives functions are thread-safe.
Q: How to get Intel® IPP threaded libraries?
Ans: While Installing Intel IPP, choose ‘custom’ installation option. Then you will get option to select threaded libraries for different architecture.
After selecting threaded libraries, selection option will get highlighted with Image may be NSFW. Clik here to view. mark and memory requirement for threaded libraries will get highlighted.
Threading, within the deprecated multi-threaded add-on packages of the Intel® IPP library, is accomplished by use of the Intel® OpenMP* library. Intel® IPP 8.0 continues the process of deprecating threading inside Intel IPP functions that was started in version 7.1. Though not installed by default, the threaded libraries can be installed so code written with these libraries will still work as before. However, moving to external threading is recommended.
Q: How can I determine the number of threads the Intel IPP creates?
Ans: You can use the function ippGetNumThreads to find the number of threads created by the Intel IPP.
Q: How do I control the number of threads the Intel IPP creates?
Ans: Call the function ippSetNumThreads to set the number of threads created.
Q: Is it possible to prevent Intel IPP from creating threads?
Ans: Yes, if you are calling the Intel IPP functions from multiple threads, it is recommended to have Intel IPP threading turned off. There are 3 ways to disable multi-threading:
Link to the non-threaded static libraries
Build and link to a custom DLL using the non-threaded static libraries
Call ippSetNumThread(1)
Q: When my application calls Intel IPP functions from a separate thread, the application hangs; how do I resolve this?
Ans: This issue occurs because the threading technology used in your application and in the Intel IPP (which has OpenMP threading) is incompatible. The ippSetNumThreads function has been developed so that threading can be disabled in the dynamic libraries. Please also check the sections above for other ways to prevent Intel IPP functions from creating threads.
Q: Which Intel IPP functions contain OpenMP* code?
Ans: "ThreadedFunctionsList.txt" file under ‘doc’ folder under product installation directory provide detailed list of threaded functions in Intel IPP Library. The list is updated in each release.
Please let us know if you have any feedback on deprecations via the feedback URL
Threading Intel® IPP Image Resize with Intel® TBB.pdf (157.18 KB) :Download Now
Introduction
The Intel® Integrated Performance Primitives (Intel® IPP) library provides a wide variety of vectorized signal and image processing functions. Intel® Threading Building Blocks (Intel® TBB) adds simple but powerful abstractions for expressing parallelism in C++ programs. This article presents a starting point for using these tools together to combine the benefits of vectorization and threading to resize images.
From Intel® IPP 8.2 onwards multi-threading (internal threaded) libraries are deprecated due to issues with performance and interoperability with other threading models, but made available for legacy applications. However, multithreaded programming is now main stream and there is a rich ecosystem of threading tools such as Intel® TBB. In most cases, handling threading at an application level (that is, external/above the primitives) offers many advantages. Many applications already have their own threading model, and application level/external threading gives developers the greatest level of flexibility and control. With a little extra effort to add threading to applications it is possible to meet or exceed internal threading performance, and this opens the door to more advanced optimization techniques such as reusing local cache data for multiple operations. This is the main reason to start deprecating internal threading in the latest releases.
Getting started with parallel_for
Intel® TBB’s parallel_for offers an easy way to get started with parallelism, and it is one of the most commonly used parts of Intel® TBB. Any for() loop in the applications, where each iteration can be done independently and the order of execution doesn’t matter. In these scenarios, Intel® TBB parallel_for is useful and takes care of most details, like setting up a thread pool and a scheduler. You supply the partitioning scheme and the code to run on separate threads or cores. More sophisticated approaches are possible. However, the goal of this article and sample code is to provide a simple starting point and not the best possible threading configuration for every situation.
Intel® TBB’s parallel_for takes 2 or 3 arguments.
parallel_for (range,body, optional partitioner )
The range, for this simplified line-based partitioning, is specified by:
blocked_range<int>(begin, end, grainsize)
This provides information to each thread about which lines of the image it is processing. It will automatically partition a range from begin to end in grainsize chunks. For Intel® TBB the grainsize is automatically adjusted when ranges don't partition evenly, so it is easy to accommodate arbitrary sizes.
The body is the section of code to be parallelized. This can be implemented separately (including as part of a class); though for simple cases it is often convenient to use a lambda expression. With the lambda approach the entire function body is part of the parallel_for call. Variables to pass to this anonymous function are listed in brackets [alg, pSrc, pDst, stridesrc_8u, …] and range information is passed via blocked_range<int>& range.
This is a general threading abstraction which can be applied to a wide variety of problems. There are many examples elsewhere showing parallel_for with simple loops such as array operations. Tailoring for resize follows the same pattern.
External Parallelization for Intel® IPP Resize
A threaded resize can be split into tiles of any shape. However, it is convenient to use groups of rows where the tiles are the width of the image.
Each thread can query range.begin(), range.size(), etc. to determine offsets into the image buffer. Note: this starting point implementation assumes that the entire image is available within a single buffer in memory.
The new image resize functions in Intel® IPP 7.1 and later versions, new approach has many advantages like
IppiResizeSpec holds precalculated coefficients based on input/output resolution combination. Multiple resizes which can be completed without recomputing them.
Separate functions for each interpolation method.
Significantly smaller executable size footprint with static linking.
Improved support for threading and tiled image processing.
Before starting resize, the offsets (number of bytes to add to the source and destination pointers to calculate where each thread’s region starts) must be calculated. Intel® IPP provides a convenient function for this purpose:
ippiResizeGetSrcOffset
This function calculates the corresponding offset/location in the source image for a location in the destination image. In this case, the destination offset is the beginning of the thread’s blocked range.
After this function it is easy to calculate the source and destination addresses for each thread’s current work unit:
This specifies how each thread works on a subset of lines of the image. Instead of using the beginning of the source and destination buffers, pSrcT and pDstT provide the starting points of the regions each thread is working with. The height of each thread's region is passed to resize via dstSizeT. Of course, in the special case of 1 thread these values are the same as for a nonthreaded implementation.
Another difference to call out is that since each thread is doing its own resize simultaneously the same working buffer cannot be used for all threads. For simplicity the working buffer is allocated within the lambda function with scalable_aligned_malloc, though further efficiency could be gained by pre-allocating a buffer for each thread.
The following code snippet demonstrates how to set up resize within a parallel_for lambda function, and how the concepts described above could be implemented together.
parallel_for( blocked_range<int>( 0, pnminfo_dst.imgsize.height, grainsize ),
[pSrc, pDst, stridesrc_8u, stridedst_8u, pnminfo_src,
pnminfo_dst, bufSize, pSpec]( const blocked_range<int>& range )
{
Ipp8u *pSrcT,*pDstT;
IppiPoint srcOffset = {0, 0};
IppiPoint dstOffset = {0, 0};
// resized region is the full width of the image,
// The height is set by TBB via range.size()
IppiSize dstSizeT = {pnminfo_dst.imgsize.width,(int)range.size()};
// set up working buffer for this thread's resize
Ipp32s localBufSize=0;
ippiResizeGetBufferSize_8u( pSpec, dstSizeT,
pnminfo_dst.nChannels, &localBufSize );
Ipp8u *localBuffer =
(Ipp8u*)scalable_aligned_malloc( localBufSize*sizeof(Ipp8u), 32);
// given the destination offset, calculate the offset in the source image
dstOffset.y=range.begin();
ippiResizeGetSrcOffset_8u(pSpec,dstOffset,&srcOffset);
// pointers to the starting points within the buffers that this thread
// will read from/write to
pSrcT=pSrc+(srcOffset.y*stridesrc_8u);
pDstT=pDst+(dstOffset.y*stridedst_8u);
// do the resize for greyscale or color
switch (pnminfo_dst.nChannels)
{
case 1: ippiResizeLanczos_8u_C1R(pSrcT,stridesrc_8u,pDstT,stridedst_8u,
dstOffset,dstSizeT,ippBorderRepl, 0, pSpec,localBuffer); break;
case 3: ippiResizeLanczos_8u_C3R(pSrcT,stridesrc_8u,pDstT,stridedst_8u,
dstOffset,dstSizeT,ippBorderRepl, 0, pSpec,localBuffer); break;
default:break; //only 1 and 3 channel images
}
scalable_aligned_free((void*) localBuffer);
});
As you can see, a threaded implementation can be quite similar to single threaded. The main difference is simply that the image is partitioned by Intel® TBB to work across several threads, and each thread is responsible for groups of image lines. This is a relatively straightforward way to divide the task of resizing an image across multiple cores or threads.
Conclusion
Intel® IPP provides a suite of SIMD-optimized functions. Intel® TBB provides a simple but powerful way to handle threading in Intel® IPP applications. Using them together allows access to great vectorized performance on each core as well as efficient partitioning to multiple cores. The deeper level of control available with external threading enables more efficient processing and better performance.
Example code: As with other Intel® IPP sample code, by downloading you accept the End User License Agreement.
In Intel® IPP 8.2 and later versions, multi-threading (internal threading) libraries are deprecated due to issues with performance and interoperability with other threading models, but made available for legacy applications. Multi-threaded static and dynamic libraries are available as a separate download to support legacy applications. For new applications development, highly recommended to use the single-threaded versions with application-level threading (as shown in the below picture).
Intel® IPP 8.2 and later versions installation will have single threaded libraries in the following directory Structure
<ipp directory>lib/ia32– Single-threaded Static and Dynamic for IA32 architecture
<ipp directory>lib/intel64 - Single-threaded Static and Dynamic for Intel 64 architecture
Static linking (Both single threaded and Multi-threaded libraries)
Windows* OS: mt suffix in a library name (ipp<domain>mt.lib)
Linux* OS and OS X*: no suffix in a library name (libipp<domain>.a)
Dynamic Linking: Default (no suffix)
Windows* OS: ipp<domain>.lib
Linux* OS: libipp<domain>.a
OS X*: libipp<domain>.dylib
Q: Does Intel® IPP supports external multi-threading? Thread safe?
Answer: Yes, Intel® IPP supports external threading as in the below picture. User has option to use different threading models like Intel TBB, Intel Cilk Plus, Windows * threads, OpenMP or PoSIX. All Intel® Integrated Performance Primitives functions are thread-safe.
Q: How to get Intel® IPP threaded libraries?
Answer: While Installing Intel IPP, choose ‘custom’ installation option. Then you will get option to select threaded libraries for different architecture.
After selecting threaded libraries, selection option will get highlighted with Image may be NSFW. Clik here to view.mark and memory requirement for threaded libraries will get highlighted.
Threading, within the deprecated multi-threaded add-on packages of the Intel® IPP library, is accomplished by use of the Intel® OpenMP* library. Intel® IPP 8.0 continues the process of deprecating threading inside Intel IPP functions that was started in version 7.1. Though not installed by default, the threaded libraries can be installed so code written with these libraries will still work as before. However, moving to external threading is recommended.
Q: How can I determine the number of threads the Intel IPP creates?
Answer: You can use the function ippGetNumThreads to find the number of threads created by the Intel IPP.
Q: How do I control the number of threads the Intel IPP creates?
Ans: Call the function ippSetNumThreads to set the number of threads created.
Q: Is it possible to prevent Intel IPP from creating threads?
Ans: Yes, if you are calling the Intel IPP functions from multiple threads, it is recommended to have Intel IPP threading turned off. There are 3 ways to disable multi-threading:
Link to the non-threaded static libraries
Build and link to a custom DLL using the non-threaded static libraries
Call ippSetNumThread(1)
Q: When my application calls Intel IPP functions from a separate thread, the application hangs; how do I resolve this?
Ans: This issue occurs because the threading technology used in your application and in the Intel IPP (which has OpenMP threading) is incompatible. The ippSetNumThreads function has been developed so that threading can be disabled in the dynamic libraries. Please also check the sections above for other ways to prevent Intel IPP functions from creating threads.
Q: Which Intel IPP functions contain OpenMP* code?
Ans: "ThreadedFunctionsList.txt" file under ‘doc’ folder under product installation directory provide detailed list of threaded functions in Intel IPP Library. The list is updated in each release.
Please let us know if you have any feedback on deprecations via the feedback URL
If you have an Intel AMT system and cannot figure out why you are getting error messages when trying to make API calls related to certain Intel AMT features, you may have a system that support Intel Standard Manageability.
You can use WS-Management commands to detect your platform's capabilities. See the Discovery Sample in the Intel AMT SDK located at <SDKRoot>\Windows\Intel_AMT\Samples\Discovery for an example. Also, messages displayed by these platforms in the MEBx and the WebUI indicate Intel Standard Manageability, instead of Intel AMT.
So what is Standard Manageability? Standard Manageability systems come with a subset of Intel AMT available features. Standard Manageability are not branded as Intel vPro. These systems are upgradeable to the full Intel AMT version capabilities.
The Standard Manageability SKU was introduced with Intel AMT Release 5.0. The following Intel AMT features are NOT included or supported on platforms with this SKU:
Remote Access, including Fast Call for Help and Remote Scheduled Maintenance
Local user-initiated call for help
Microsoft Network Access Protection (NAP)
Wireless connections to the Manageability Engine
Access Monitor
Alarm Clock (out-of-band & in-band from release 9.0)
KVM
Note: From Intel AMT Release 6.0, the Access Monitor and Microsoft Network Access Protection (NAP) are supported in this SKU.
You can detect the SKU of a platform by looking for the following CIM objects:
On an Intel AMT SKU, there will be:
An instance of CIM_RegisteredProfile with a RegisteredName equal to
“Intel(r) AMT” and an InstanceID equal to
“Intel(r) ME:Intel(r) AMT”
An instance of CIM_ComputerSystem with an ElementName of
“Intel(r) AMT Subsystem”
On a Standard Manageability SKU, there will be:
An instance of CIM_RegisteredProfile with a RegisteredName equal to
“Intel(r) Std. Mgt.” and an InstanceID equal to
“Intel(r) ME:Intel(r) Std. Mgt.”
An instance of CIM_ComputerSystem with an ElementName of
“Intel(r) Std. Mgt. Subsystem”.
For more information, please see the Support for Other Intel Platforms section of the Intel AMT Implementation and Reference Guide.
В этом образце кода демонстрируется использование Intel® RealSense™ SDK для Windows* в классическом приложении на C#/WPF. Образец приложения под названием BlockHeadиспользует три интересных функции Intel RealSense SDK:
получает и отображает цветное изображение с камеры RGB;
получает оценочные данные о расположении лица и положении головы;
получает и анализирует данные о выражении лица.
(Примечание. Для реализации полной функциональности этого приложения требуется направленная на пользователя трехмерная камера.)
Как показано на рис. 1, приложение отображает поток цветовых данных в элементе управления WPF Image и в реальном времени накладывает мультипликационное изображение на лицо пользователя.
Image may be NSFW. Clik here to view. Рисунок 1.Наложение мультипликационного изображения на лицо пользователя
Мультипликационное изображение программным образом формируется в реальном времени на основе данных, получаемых от SDK.
Изображение масштабируется в соответствии с лицом пользователя (уменьшается, когда пользователь отодвигает голову от камеры, и увеличивается, когда пользователь приближает голову к камере) на основе информации о прямоугольной зоне лица.
Изображение наклоняется влево и вправо в зависимости от положения головы пользователя (поворот вокруг продольной оси).
Содержимое изображения изменяется на основе получения и анализа данных о выражении лица пользователя (см. рис. 2).
Image may be NSFW. Clik here to view. Рисунок 2.Распознавание улыбки, высунутого языка, воздушного поцелуя и открытого рта в реальном времени
Подробные сведения
Для этого простого демонстрационного приложения графика была создана в графическом редакторе и записана в виде PNG-файлов. Вместо этих изображений можно использовать высококачественные изображения с различными уровнями прозрачности, фотографии друзей, карикатуры и прочее для достижения более интересных визуальных эффектов.
Различные преобразования (например, ScaleTransform, RotateTransform) применяются к объекту изображения для изменения его положения в соответствии с данными Intel RealSense SDK о положении головы. Эти данные включают расположение лица, расположение головы и данные распознавания выражения лица.
SDK может фиксировать около 20 различных выражений лица, которые затем можно анализировать в приложении. В этом приложении основное внимание уделяется выражениям лица с различными очертаниями рта: EXPRESSION_KISS, EXPRESSION_MOUTH_OPEN, EXPRESSION_SMILE и EXPRESSION_TONGUE_OUT. При этом можно без труда расширить возможности приложения, чтобы также использовать информацию о положении головы, глаз и бровей для определения выражения лица.
Ознакомьтесь
Чтобы узнать больше об этом приложении, просмотреть код и развить его, добавив более интересные возможности, опирающиеся на Intel RealSense SDK, загрузите этот пакет здесь.
Брайан Браун — инженер по разработке программных приложений в подразделении Developer Relations корпорации Intel. Его профессиональный опыт охватывает создание программного обеспечения и электроники, а также проектирование систем. Среди интересующих его направлений — применение технологий естественного взаимодействия и интерфейсов между компьютером и мозгом. Он активно участвует в нескольких программах разработки различных передовых технологиях в этих областях.
Аннотация
Microsoft Direct3D* 12 — важный шаг в развитии технологий игр на ПК. В новой версии разработчики получают более мощные средства контроля над своими играми, могут эффективнее использовать ресурсы ЦП.
На конференции GDC 2014 корпорация Microsoft объявила важную новость для всего рынка игр для ПК в 2015 году — выпуск новой версии Direct3D, а именно версии 12. В D3D 12 создатели вернулись к низкоуровневому программированию: оно дает разработчикам игр более полный контроль и много новых интересных возможностей. Группа разработки D3D 12 старается снизить издержки при задействовании ЦП и повысить масштабируемость, чтобы полнее нагружать ядра ЦП. Цель состоит в повышении эффективности и производительности консольных API, чтобы игры могли эффективнее использовать ресурсы ЦП/ГП. В мире игр для ПК большую часть работы, а то и всю работу часто выполняет один-единственный поток ЦП. Другие потоки заняты только операционной системой и другими системными задачами. Существует совсем немного действительно многопоточных игр для ПК. Microsoft стремится изменить эту ситуацию в наборе D3D 12, который является надстройкой над функциональностью рендеринга D3D 11. Это означает, что на всех современных ГП можно запускать D3D 12, поскольку при этом будут эффективнее задействованы современные многоядерные ЦП и ГП. Для использования всех преимуществ D3D 12 не нужно покупать новый графический процессор. Действительно, у игр на ПК с процессором Intel® очень яркое будущее.
1.0 Хорошо забытое старое
Низкоуровневое программирование широко применяется в отрасли консолей, поскольку характеристики и устройство каждой модели консолей неизменны. Разработчики игр могут подолгу отлаживать свои игры, чтобы выжать всю возможную производительность из Xbox One* или PlayStation* 4. С другой стороны, ПК по своей природе — это гибкая платформа с бесчисленным множеством разновидностей и вариаций. При планировании разработки новой игры для ПК требуется учитывать очень много факторов. Высокоуровневые API, такие как OpenGL* и Direct3D*, помогают упростить разработку. Эти API выполняют всю «черную работу», поэтому разработчики могут сосредоточиться собственно на играх. Проблема же заключается в том, что и API, и (в несколько меньшей степени) драйверы достигли уже такого уровня сложности, что они могут увеличить объем потребляемых ресурсов при рендеринге кадров, что приводит к снижению производительности. Именно здесь на сцену выходит низкоуровневое программирование.
Первая эпоха низкоуровневого программирования на ПК окончилась вместе с прекращением использования MS-DOS*. На смену пришли API разных поставщиков. После 3Dglide* компании 3DFX* появились такие API, как Direct3D. ПК проиграли в производительности, получив взамен столь необходимую гибкость и удобство. Рынок оборудования стал крайне сложным, с огромным множеством доступных аппаратных решений. Время разработки увеличилось, поскольку разработчики, естественно, стремились к тому, чтобы в их игры могли бы играть все пользователи. При этом перемены произошли не только на стороне программ, но и в оборудовании: эффективность ЦП с точки зрения потребления электроэнергии стала важнее, чем их производительность. Теперь вместо того, чтобы гнаться за увеличением тактовой частоты, больше внимания уделяется использованию нескольких ядер и потоков ЦП, параллельному рендерингу на современных ГП. Настала пора применить в области игр для ПК некоторые методики, используемые в консольных играх. Пора эффективнее, полнее использовать все доступные ядра и потоки. Словом, пора перейти в XXI век в мире игр для ПК.
1.1 Ближе к железу
Чтобы приблизить игры к «железу», нужно снизить сложность и размер API и драйвера. Между оборудованием и самой игрой должно быть меньше промежуточных уровней. Сейчас API и драйвер тратят слишком много времени на преобразование команд и вызовов. Некоторые или даже многие эти процедуры будут снова отданы разработчикам игр. За счет снижения издержек в D3D 12 повысится производительность, а за счет уменьшения количества промежуточных уровней обработки между игрой и оборудованием ГП игры будут быстрее работать и лучше выглядеть. Разумеется, у медали есть и обратная сторона: некоторые разработчики могут не желать заниматься областями, которые ранее были под контролем API, например программировать управление памятью ГП. Пожалуй, в этой области слово за разработчиками игровых движков, но, впрочем, только время расставит все на свои места. Поскольку выпуск D3D 12 состоится еще не скоро, имеется предостаточно времени для размышлений на эту тему. Итак, как же будут выполнены все эти заманчивые обещания? Главным образом с помощью новых возможностей. Это объекты состояния конвейера, списки команд, наборы и кучи.
2.0 Объект состояния конвейера
Прежде чем рассказывать об объекте состояния конвейера (PSO), сначала рассмотрим контекст рендеринга D3D 11, а потом перейдем к изменениям в D3D 12. На рис. 1 показан контекст рендеринга D3D 11 в том виде, в котором его представил Макс Мак-Маллен (Max McMullen), руководитель по разработке D3D, на конференции BUILD 2014 в апреле 2014 года.
Крупные толстые стрелки указывают отдельные состояния конвейера. В соответствии с потребностями игры каждое такое состояние можно получить или задать. Прочие состояния в нижней части - фиксированные состояния функций, такие как поле зрения или прямоугольник обрезки. Другие важные компоненты этой схемы будут пояснены в дальнейших разделах данной статьи. При обсуждении PSO нас интересует только левая часть схемы. В D3D 11 удалось снизить издержки ЦП по сравнению с D3D 9 благодаря малым объектам состояния, но по-прежнему требовалась дополнительная работа драйвера, который должен был брать эти малые объекты состояния и сочетать их с кодом ГП во время рендеринга. Назовем эту проблему издержками аппаратного несоответствия. Теперь посмотрим еще на одну схему с конференции BUILD 2014, показанную на рис. 2.
Direct3D 11 — издержки состояния конвейера
Малые объекты состояния à издержки аппаратного несоответствия
Image may be NSFW. Clik here to view. Рисунок 2. В конвейере D3D 11 с малыми объектами состояния часто возникают издержки аппаратного несоответствия
На левой стороне показан конвейер в стиле D3D 9: здесь показано, что использует приложение для выполнения своей работы. Оборудование, показанное на правой стороне схемы на рис. 2, необходимо программировать. Состояние 1 представляет код шейдера. Состояние 2 — это сочетание растеризатора и потока управления, связывающего растеризатор с шейдерами. Состояние 3 — связь между смешением и пиксельным шейдером. Шейдер вертексов D3D влияет на аппаратные состояния 1 и 2, растеризатор — на состояние 2, пиксельный шейдер — на состояния с 1 по 3 и так далее. Драйверы в большинстве случаев не отправляют вызовы одновременно с приложением. Они предпочитают записывать вызовы и дожидаться выполнения работы, чтобы можно было определить, что на самом деле нужно приложению. Это означает дополнительные издержки для ЦП, поскольку старые и устаревшие данные помечаются как «непригодные». Поток управления драйвера проверяет состояние каждого объекта во время рендеринга и программирует оборудование так, чтобы соответствовать заданному игрой состоянию. При этой дополнительной работе возможно исчерпание ресурсов и возникновение затруднений. В идеале, как только игра задает состояние конвейера, драйвер тут же «знает», что нужно игре, и сразу программирует оборудование. На рис. 3 показан конвейер D3D 12, который делает именно это с помощью так называемого объекта состояния конвейера (PSO).
На рис. 3 показан упорядоченный процесс с уменьшенными издержками. Один PSO с информацией о состоянии для каждого шейдера может за одну копию задать все аппаратные состояния. Вспомните, что некоторые состояния были помечены как «прочие» в контексте рендеринга D3D 11. Разработчики D3D 12 осознали важность уменьшения размера PSO и предоставления игре возможности смены цели рендеринга, не затрагивая скомпилированный PSO. Такие вещи, как поле зрения и прямоугольник обрезки, остались отдельными, они программируются вне остального конвейера (рис. 4).
Контекст рендеринга: объект состояния конвейера (PSO)
Вместо того чтобы задавать и прочитывать каждое состояние по отдельности, мы получили единую точку, тем самым полностью избавившись от издержек аппаратного несоответствия. Приложение задает PSO нужным образом, а драйвер получает команды API и преобразует их в код ГП без дополнительных издержек, связанных с управлением потоком. Такой подход (более близкий к «железу») означает, что для обработки команд рендеринга требуется меньше циклов, производительность возрастает.
3.0 Привязка ресурсов
Перед рассмотрением изменений в привязке ресурсов вспомним, какая модель привязки ресурсов использовалась в D3D 11. На рис. 5 снова показана схема контекста рендеринга: слева — объект состояния конвейера D3D 12, а справа — модель привязки ресурсов D3D 11.
Контекст отрисовки: объект состояния конвейера (PSO)
На рис. 5 принудительные точки привязки находятся справа от каждого шейдера. Принудительная модель привязки означает, что у каждого этапа в конвейере есть определенные ресурсы, на которые можно сослаться. Эти точки привязки ссылаются на ресурсы в памяти ГП. Это могут быть текстуры, цели рендеринга, неупорядоченные представления доступа (UAV) и так далее. Привязки ресурсов используются уже давно, они появились даже раньше, чем D3D. Цель в том, чтобы «за кадром» обработать множество свойств и помочь игре в эффективной отправке команд рендеринга. При этом системе необходимо выполнить анализ множества привязок в трех основных областях. В следующем разделе рассматриваются эти области и их оптимизация в D3D 12.
3.1 Опасности, связанные с ресурсами
Опасности обычно связаны с переходами, например с перемещением от цели рендеринга к текстуре. Игра может отрисовать кадр, который должен стать картой среды для сцены. Игра завершает рендеринг карты среды и теперь хочет использовать ее в качестве текстуры. В ходе этого процесса и среда выполнения, и драйвер отслеживают все ресурсы, привязанные как цели рендеринга или как текстуры. Если среда выполнения или драйвер видят какой-либо ресурс, привязанный одновременно и как цель рендеринга, и как текстура, они отменяют более старую по времени привязку и сохраняют более новую. За счет этого игра может переключаться нужным образом, а набор программного обеспечения управляет этим переключением «за кадром». Драйвер также должен очистить конвейер ГП, чтобы можно было использовать цель рендеринга в качестве текстуры. В противном случае пиксели будут прочитаны до того, как они будут удалены из ЦП, и полученный результат будет несогласованным. Собственно говоря, опасность — это все, для чего требуется дополнительная работа ГП с целью получения согласованных данных.
Как и в других усовершенствованиях в D3D 12, решение здесь состоит в том, чтобы предоставить игре больше возможностей управления. Почему API и драйвер должны выполнять всю работу и отслеживание, когда это всего лишь один момент в обработке кадра? Для переключения от одного ресурса к другому требуется около 1/60 секунды. Можно снизить издержки, передав управление игре, тогда дополнительное время будет израсходовано только один раз, когда игра осуществляет переключение ресурсов (рис. 6).
Рисунок 6. API ограничителя ресурсов, добавленный в D3D 12
API ограничителя ресурсов, показанный на рис. 6, объявляет ресурс, его начальное использование и целевое использование, после чего следует вызов, чтобы сообщить выполняемой среде и драйверу о переключении. Переключение становится явным вместо отслеживаемого в рендеринге кадра со множеством условной логики, оно осуществляется один раз за кадр или с такой частотой, с которой оно требуется в игре.
3.2 Управление резидентностью ресурсов
D3D 11 (и более старые версии) работают так, как если бы все вызовы были в очереди. Игра считает, что API немедленно выполняет вызов. Но на самом деле это не так. ГП поддерживает очередь команд, в которой все команды откладываются и выполняются позднее. При этом обеспечивается распараллеливание вычислений и более эффективное задействование ГП и ЦП, но требуется отслеживание ссылок и их учет. На учет и отслеживание расходуется достаточно много ресурсов ЦП.
Чтобы решить эту проблему, игра получает явное управление жизненным циклом ресурсов. В D3D 12 больше не скрывается очередь ГП. Для отслеживания выполнения на ГП добавлен API Fence. Игра может в заданный момент (например, один раз за кадр) проверить, какие ресурсы больше не нужны, и затем высвободить занимаемую ими память. Больше не требуется отслеживать ресурсы в течение всего рендеринга кадра с помощью дополнительной логики, чтобы высвобождать ресурсы и память.
3.3 Зеркальное копирование состояния
После оптимизации трех описанных выше областей был обнаружен дополнительный элемент, способный обеспечить прирост производительности, хотя и не столь заметный. Когда задана точка привязки, среда выполнения отслеживает эту точку, чтобы игра могла позднее вызвать Getи узнать, что привязано к конвейеру. Создается зеркальная копия точки привязки. Эта функция выполняет промежуточную работу, чтобы разделенное на компоненты программное обеспечение могло определить текущее состояние контекста рендеринга. После оптимизации привязки ресурсов зеркальные копии состояний больше не нужны. Помимо упразднения управления потоком из трех описанных выше областей также удалены операции Get для зеркальных копий.
4.0 Кучи и таблицы
Осталось рассмотреть еще одно важное изменение привязки ресурсов. В конце раздела 4 будет показан весь контекст рендеринга D3D 12. Новый контекст рендеринга D3D 12 — первый шаг на пути к повышению эффективности использования ЦП в API.
4.1 Привязка избыточных ресурсов
Проведя анализ нескольких игр, разработчики D3D заметили, что обычно в играх в множестве кадров используется одна и та же последовательность команд. Не только команды, но и привязки остаются точно такими же из кадра в кадр. ЦП создает последовательность привязок (скажем, 12) для рисования объекта в кадре. Зачастую ЦП приходится создавать эти же 12 привязок еще раз для следующего кадра. Почему же не поместить эти привязки в кэш? Разработчикам игр можно предоставить команду, указывающую на кэш, чтобы можно было многократно использовать одинаковые привязки.
Вразделе 3 мы обсудили очереди. Когда осуществляется вызов, игра исходит из того, что API немедленно выполняет этот вызов. Но на самом деле это не так. Команды помещаются в очередь, все содержимое которой откладывается и выполняется позднее в ГП. Поэтому если изменить одну из этих 12 привязок, о которых мы говорили ранее, то драйвер скопирует все 12 привязок в новое место, изменит копию, затем даст графическому процессору команду начать использовать скопированные привязки. Обычно у большинства из этих 12 привязок значения бывают статическими, а обновление требуется лишь для нескольких динамических значений. Когда игре нужно внести частичные изменения в эти привязки, она копирует все 12 штук, из-за чего наблюдается чрезмерный расход ресурсов ЦП при незначительных изменениях.
4.2 Дескрипторы
Что такое дескриптор? Коротко говоря, это фрагмент данных, определяющий параметры ресурсов. По сути, это то, из чего состоит объект представления D3D 11. Управление жизненным циклом на уровне операционной системы отсутствует. Это просто данные в памяти ГП. Здесь содержится информация о типе и формате, счетчик MIP для текстур и указатель на пиксельные данные. Дескрипторы находятся в центре новой модели привязки ресурсов.
Когда представление задано в D3D 11, оно копирует дескриптор в текущее расположение в памяти ГП, откуда прочитываются дескрипторы. Если задать новое представление в этом же расположении, в D3D 11 дескрипторы будут скопированы в новое расположение в памяти, а ГП в следующем вызове команды рендеринга получит указание читать дескрипторы из этого нового расположения. В D3D 12 игра или приложение получают явное управление созданием дескрипторов, их копированием и пр.
Кучи (рис. 8) являются просто очень большим массивом дескрипторов. Можно повторно использовать дескрипторы из предыдущих вызовов рендеринга и кадров. Можно создавать новые при необходимости. Вся разметка находится под управлением игры, поэтому при управлении кучей почти не возникают издержки. Размер кучи зависит от архитектуры ГП. В устаревших маломощных ГП размер может быть ограничен 65 тысячами, а в более мощных ГП ограничение будет определяться объемом памяти. В менее мощных ГП возможно превышение размера кучи. В D3D 12 поддерживается несколько куч и переключение от одной кучи дескрипторов к другой. Тем не менее при переключении между кучами в некоторых ГП происходит сброс данных, поэтому использованием этой функции лучше не злоупотреблять.
Итак, как сопоставить код шейдеров с определенными дескрипторами или наборами дескрипторов? Ответ – с помощью таблиц.
4.4 Таблицы
Таблицы содержат индекс начала и размер в куче. Они являются точками контекста, но не объектами API. При необходимости у каждого этапа шейдера может быть одна или несколько таблиц. Например, шейдер вертекса для вызова рендеринга может содержать таблицу, указывающую на дескрипторы со смещением с 20 по 32 в куче. Когда начнется работа над следующим вызовом рендеринга, смещение может измениться на 32—40.
Используя существующее оборудование, D3D 12 может обрабатывать несколько таблиц на каждое состояние шейдера в PSO. Можно поддерживать одну таблицу лишь с теми данными, которые часто изменяются между вызовами, а вторую таблицу — со статическими данными, неизменными для нескольких вызовов и для нескольких кадров. Это позволит избежать копирования дескрипторов из одного вызова в следующий. Тем не менее у старых ГП действует ограничение в одну таблицу на каждый этап шейдера. Поддержка нескольких таблиц возможна в современном и в перспективном оборудовании.
4.5 Эффективность и работа без привязки
Кучи дескрипторов и таблицы применяются в D3D для рендеринга без привязки, причем с возможностью задействования всех аппаратных ресурсов ПК. В D3D 12 поддерживаются любые устройства — от маломощных «систем на кристалле» до высокопроизводительных дискретных графических адаптеров. Благодаря такому универсальному подходу разработчики игр получают разнообразные возможности управления привязками. Кроме того, новая модель включает множественные обновления частоты. Поддерживаются кэшированные таблицы статических привязок с возможностью многократного использования и динамические таблицы для данных, изменяющихся в каждом вызове рендеринга. Таким образом, необходимость копировать все привязки для каждого нового вызова рендерингаотпадает.
4.6 Обзор контекста отрисовки
На рис. 10 показан контекст рендеринга с изменениями D3D 12, которые мы уже успели обсудить. Также показан новый объект состояния конвейера и упразднение вызовов Get, но сохранились явные точки привязки D3D 11.
Давайте удалим последние остатки контекста рендеринга D3D 11 и добавим таблицы дескрипторов и кучи. Теперь у нас появились таблицы для каждого этапа шейдера (или несколько таблиц, как показано для пиксельного шейдера).
Тонко настраиваемые объекты состояния упразднены, их заменил объект состояния конвейера. Удалено отслеживание опасностей и зеркальное копирование состояния. Принудительные точки привязки заменены на управляемые приложением или игрой объекты памяти. ЦП используется более эффективно, издержки снижены, упразднены поток управления и логика и в API, и в драйвере.
5.0 Наборы
Мы завершили рассмотрение нового контекста рендеринга в D3D 12 и увидели, каким образом D3D 12 передает управление игре, приближая ее к «железу». Но этим возможности D3D 12 по повышению эффективности API не ограничиваются. В API по-прежнему существуют издержки, влияющие на производительность, и существуют дополнительные способы повысить эффективность использования ЦП. Как насчет последовательностей команд? Сколько существует повторяющихся последовательностей и как сделать их более эффективными?
5.1 Избыточные команды рендеринга
При изучении команд рендеринга в каждом кадре разработчики D3D в Microsoft обнаружили, что при переходе от одного кадра к другому происходит добавление или удаление только 5—10 % последовательностей команд. Остальные последовательности команд используются во множестве кадров. Итак, ЦП в течение 90—95 % времени своей работы повторяет одни и те же последовательности команд!
Как здесь повысить эффективность? И почему в D3D это не было сделано до сих пор? На конференции BUILD 2014 Макс Мак-Маллен сказал: «Очень сложно создать универсальный и надежный способ записывать команды. Такой способ, чтобы он работал всегда одинаково для разных ГП, с разными драйверами и при этом работал бы быстро». Игре требуется, чтобы все записанные последовательности команд выполнялись так же быстро, как отдельные команды. Что изменилось? D3D. Благодаря новым объектам состояния конвейера, кучам дескрипторов и таблицам состояние, необходимое для записи и воспроизведения команд, значительно упростилось.
5.2 Что такое наборы?
Наборы — это небольшие списки команд, которые один раз записываются, после чего их можно многократно использовать без каких-либо ограничений в одном кадре или в нескольких кадрах. Наборы можно создавать в любом потоке и использовать сколько угодно раз. Наборы не привязываются к состоянию объектов PSO. Это означает, объекты PSO могут обновлять таблицу дескрипторов, а при запуске набора для разных привязок игра будет получать разные результаты. Как и в случае с формулами в электронных таблицах Excel*, математика всегда одинаковая, а результат зависит от исходных данных. Существуют определенные ограничения, чтобы гарантировать эффективную реализацию наборов драйвером. Одно из таких ограничений состоит в том, что никакая команда не может сменить цель рендеринга. Но остается еще множество команд, которые можно записать и воспроизводить.
Слева на рис. 12 — пример контекста рендеринга, последовательность команд, созданных в ЦП и переданных на ГП для выполнения. Справа — два пакета, содержащих записанную последовательность команд для многократного использования в разных потоках. По мере выполнения команд ГП достигает команды на выполнение набора. После этого воспроизводится записанный набор. По завершении ГП возвращается к последовательности команд, продолжает и находит следующую команду выполнения набора. После этого прочитывается и воспроизводится второй набор, после чего выполнение продолжается.
5.3 Эффективность кода
Мы рассмотрели управление потоком в ГП. Теперь посмотрим, каким образом наборы упрощают код.
Пример кода без наборов
Перед нами страница настройки, задающая состояние конвейера и таблицы дескрипторов. Затем идут два вызова рендеринга объектов. В обоих случаях используется одинаковая последовательность команд, различаются только константы. Это типичный код D3D 11.
Теперь рассмотрим эту же последовательность команд с пакетами в D3D 12. Первый вызов, показанный ниже, создает набор. Это может быть сделано в любом потоке. На следующем этапе создается последовательность команд. Это такие же команды, как и в предыдущем примере
// Создание набора
pDevice->CreateCommandList(D3D12_COMMAND_LIST_TYPE_BUNDLE, pBundleAllocator, pPSO, pDescriptorHeap, &pBundle);
Рисунок 17. Образец кода с созданием набора
// Запись команд
pBundle->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
pBundle->SetShaderResourceViewTable(D3D12_SHADER_STAGE_PIXEL, 0, 1);
pBundle->DrawInstanced(6, 1, 0, 0);
pBundle->SetShaderResourceViewTable(D3D12_SHADER_STAGE_PIXEL, 1, 1);
pBundle->DrawInstanced(6, 1, 6, 0);
pBundle->Close();
Рисунок 18. Образец кода с записью набора
В примерах кода на рис. 17 и 18 достигается такой же результат, как в коде без наборов на рис. 14—16. Хорошо видно, что наборы позволяют существенно сократить количество вызовов, необходимое для выполнения одной и той же задачи. ГП при этом выполняет точно такие же команды и получает такой же результат, но гораздо эффективнее.
6.0 Списки команд
Вы уже знаете, каким образом в D3D 12 повышается эффективность использования ЦП и предоставляются более широкие возможности разработчикам с помощью наборов, объектов состояния конвейера, куч дескрипторов и таблиц. Модель объектов состояния конвейера и дескрипторов поддерживает наборы, которые, в свою очередь, используются для широко распространенных и часто повторяющихся команд. Такой упрощенный и «приближенный к железу» подход снижает издержки и позволяет эффективнее использовать ЦП. Ранее мы упомянули, что в играх для ПК большую часть работы, а то и всю работу выполняет только один поток, а остальные потоки занимаются другими системными задачами и процессами ОС. Добиться эффективного использования нескольких ядер или потоков игрой для ПК не так просто. Зачастую для реализации многопоточности в игре требуются существенные затраты ресурсов и труда. Разработчики D3D собираются изменить это положение дел в D3D 12.
6.1 Параллельное создание команд
Как уже неоднократно упоминалось выше, отложенное выполнение команд — это модель, при которой создается ощущение, что каждая команда выполняется немедленно, но на самом деле команды помещаются в очередь и выполняются позднее. Эта функция сохраняется в D3D 12, но теперь она является прозрачной для игры. Не существует немедленного контекста, поскольку все откладывается. Потоки могут создавать команды параллельно для формирования списков команд, которые подаются в объект API, который называется очередью команд. ГП не будет выполнять команды, пока они не будут отправлены с помощью очереди команд. Очередь — это порядок следования команд, которые указываются в списке. Чем отличаются списки команд от наборов? Списки команд создаются и оптимизируются, поэтому несколько потоков могут одновременно создавать команды. Списки команд используются однократно, а затем удаляются из памяти; на месте удаленного списка команд записывается новый список. Наборы предназначены для многократного выполнения часто используемых команд рендеринга в одном или в нескольких кадрах.
В D3D 11 была сделала попытка распараллелить обработку команд; эта функция называлась отложенным контекстом. Но из-за издержек цели по повышению производительности тогда не были достигнуты. Подробный анализ показал множество мест с избыточной последовательной обработкой, что привело к неэффективному распределению нагрузки по ядрам ЦП. Часть издержек с последовательной обработкой была устранена в D3D 12 с помощью средств повышения эффективности использования ЦП, описанных в разделах 2—5.
6.2 Списки и очередь
Представьте, что два потока создают список команд рендеринга. Одна последовательность должна быть выполнена перед другой. При наличии опасностей один поток использует ресурс в качестве текстуры, а другой поток использует этот же ресурс в качестве цели рендеринга. Драйвер должен проанализировать использование ресурсов во время рендеринга и устранить опасности, обеспечив согласованность данных. Отслеживание опасностей — одна из областей с последовательными издержками в D3D 11. В D3D 12 за отслеживание опасностей отвечает игра, а не драйвер.
D3D 11 поддерживается несколько отложенных контекстов, но при их использовании возникает сопутствующая нагрузка. Драйвер отслеживает состояние для каждого ресурса. Поэтому, как только начата запись команд для отложенного контекста, драйвер должен выделять память для отслеживания состояния каждогоиспользуемого ресурса. Память занята, пока идет создание отложенного контекста. По завершении драйвер должен удалить из памяти все объекты отслеживания. Из-за этого возникают ненужные издержки. Игра объявляет максимальное количество списков команд, которые можно создать параллельно на уровне API. После этого драйвер упорядочивает и заранее выделяет все объекты отслеживания в одном согласованном объекте памяти.
В D3D 11 распространены динамические буферы (буфер контекста, вертексов и т. д.), но «за кадром» остается множество экземпляров удаленных буферов отслеживания памяти. Например, параллельно могут быть созданы два списка команд, и вызвана функция MapDiscard. После отправки списка драйвер должен вмешаться во второй список команд, чтобы исправить информацию об удаленном буфере. Как и в приведенном выше примере с отслеживанием опасностей, здесь возникают значительные издержки. В D3D 12 управление переименованием передано игре, динамического буфера больше нет. Игра получила полное управление. Она создает собственные распределители и может делить буфер на части по мере необходимости. Поэтому команды могут указывать на явным образом заданные точки в памяти.
Как мы говорили вразделе 3.1,среда выполнения и драйвер отслеживают жизненный цикл ресурсов в D3D 11. Для этого требуется подсчет и отслеживание ресурсов, все операции должны быть сделаны во время отправки. В D3D 12 игра управляет жизненным циклом ресурсов и обработкой опасностей, благодаря чему устраняются издержки последовательной обработки и повышается эффективность использования ЦП. Параллельное создание команд работает эффективнее в D3D 12 с учетом оптимизации в четырех описанных областях, что расширяет возможности распараллеливания нагрузки на ЦП. Кроме того, разработчики D3D создают новую модель драйверов WDDM 2.0 и планируют реализовать дополнительные меры по оптимизации, чтобы снизить нагрузку при отправке списков команд.
6.3 Поток очереди команд
Очередь команд
Image may be NSFW. Clik here to view. Рисунок 19. Очередь команд с двумя параллельно создаваемыми списками команд и двумя наборами повторяющихся команд
На рис. 19 показана схема набора израздела 5.2,но с многопоточностью. Очередь команд, показанная слева, представляет собой последовательность событий, отправленных на ГП. Списки команд находятся посередине, а справа — два набора, записанные перед началом сценария. Начиная со списков команд, создаваемых параллельно для разных фрагментов сцены, завершается запись списка команд 1, этот список отправляется в очередь команд, и ГП начинает его выполнять. Параллельно запускается процедура управления потоком очереди команд, а список команд 2 записывается в потоке 2. Пока ГП выполняет список команд 1, поток 2 завершает создание списка команд 2 и отправляет его в очередь команд. Когда очередь команд завершает выполнение списка команд 1, она последовательно переходит к списку команд 2. Очередь команд — это последовательность, в которой ГП должен выполнять команды. Хотя список команд 2 был создан и отправлен в ГП до того, как ГП завершил выполнение списка команд 1, список команд 2 будет выполнен только после завершения выполнения списка команд 1. В D3D 12 поддерживается более эффективное распараллеливание для всего процесса.
7.0 Динамические кучи
Как мы уже говорили выше, игра управляет переименованием ресурсов для распараллеливания создания команд. Кроме того, в D3D 12 упрощено переименование ресурсов. В D3D 11 буферы были разделены по типам: были буферы вертексов, констант и индексов. Разработчики игр запросили возможность использовать зарезервированную память так, как им заблагорассудится. И команда D3D выполнила эту просьбу. В D3D 12 разделение буферов по типам отсутствует. Буфер — это просто объем памяти, выделяемый игрой по мере необходимости в размере, необходимом для кадра (или нескольких кадров). Можно даже использовать распределитель кучи с делением по мере необходимости, что повышает эффективность процессов. В D3D 12 также применяется стандартное выравнивание. ГП сможет прочесть данные, если в игре используется стандартное выравнивание. Чем выше уровень стандартизации, тем проще создавать содержимое, работающее с разными моделями ЦП, ГП и другого оборудования. Распределение памяти также является постоянным, поэтому ЦП всегда знает нужный адрес. При этом также повышается эффективность параллельного использования ЦП: поток может направить ЦП на нужный адрес в памяти, после чего ЦП определяет, какие данные нужны для кадра.
В верхней части рис. 20 показана модель D3D 11 с типизацией буферов. В нижней части показана новая модель D3D 12, где игра управляет кучей. Вместо выделения отдельной части памяти для буфера каждого типа используется единый постоянный фрагмент памяти. Размер буфера также настраивается игрой на основе потребностей рендеринга текущего кадра или даже нескольких последующих кадров.
8.0 Параллельная работа ЦП
Пора собрать все воедино и продемонстрировать, каким образом новые возможности D3D 12 позволяют создать действительно многопоточную игру для ПК. В D3D 12 поддерживается параллельное выполнение нескольких задач. Списки команд и наборы предоставляют возможность параллельного создания и выполнения команд. Наборы позволяют записывать команды и многократно запускать их в одном или в нескольких кадрах. Списки команд могут быть созданы в нескольких потоках и переданы в очередь команд для выполнения графическим процессором. И наконец, буферы с постоянным распределением параллельно создают динамические данные. Параллельная работа поддерживается и в D3D 12, и в WDDM 2.0. В D3D 12 устранены ограничения прежних версий D3D, разработчики могут распараллеливать свои игры или движки любым целесообразным способом.
Профилирование в D3D11
Image may be NSFW. Clik here to view. Рисунок 21. Типичная параллельная обработка в D3D 11. Поток 0 выполняет почти всю работу, остальные потоки используются незначительно
На схеме на рис. 21 показана типичная игровая нагрузка в D3D 11. Логика приложения, среда выполнения D3D, DXGKernel, KMD и текущая работа задействуют ЦП с четырьмя потоками. Поток 0 выполняет большую часть работы. Потоки 1—3 практически не используются: в них попадают только логика приложения и среда выполнения D3D 11, создающая команды рендеринга. Драйвер пользовательского режима вообще не создает команд для этих потоков в силу особенностей устройства D3D 11.
Профилирование в D3D12
Image may be NSFW. Clik here to view. Рисунок 22. Здесь показана такая же нагрузка, как на рис. 21, но в D3D 12. Нагрузка равномерно распределяется по всем 4 потокам, а с учетом других мер по повышению эффективности в D3D 12 скорость выполнения нагрузки значительно увеличена
Теперь рассмотрим такую же нагрузку в D3D 12 (рис. 22). Логика приложения, среда выполнения D3D, DXGKernel, KMD и текущая работа также задействуют ЦП с четырьмя потоками. Но здесь работа равномерно распределяется по всем потокам за счет оптимизации в D3D 12. Поскольку команды создаются параллельно, выполняемая среда D3D также работает параллельно. Издержки ядра значительно снижены за счет оптимизации ядра в WDDM 2.0. UMD работает во всех потоках, а не только в потоке 0, что означает настоящее распараллеливание создания команд. И наконец, наборы заменяют логику избыточного изменения состояния в D3D 11 и ускоряют работу логики приложения.
На рис. 23 показано сравнение обеих версий. Поскольку уровень фактической параллельности достаточно высок, мы видим относительно равное использование ЦП потоком 0 и потоками 1—3. Потоки 1—3 выполняют больше работы, поэтому в столбце «Только графика» видно увеличение. Кроме того, благодаря снижению нагрузки в потоке 0 и новым мерам по повышению эффективности среды выполнения и драйвера общую нагрузку на ЦП удалось снизить примерно на 50 %. Если рассмотреть столбец «Приложение плюс графика», здесь также распределение нагрузки между потоками стало более равномерным, а использование ЦП снизилось примерно на 32 %.
9.0 Заключение
В D3D 12 повышена эффективность использования ЦП за счет более крупных объектов состояния конвейера. Вместо того чтобы задавать и прочитывать каждое состояние по отдельности, разработчики получили единую точку приложения сил, тем самым полностью избавившись от издержек аппаратного несоответствия. Приложение задает PSO, а драйвер получает команды API и преобразует их в код ГП. Новая модель привязки ресурсов не имеет издержек, вызванных логикой управления потоком, которая теперь не нужна.
За счет использования куч, таблиц и наборов D3D 12 обеспечивает более эффективное использование ЦП и более высокую масштабируемость. Вместо явных точек привязки используются управляемые приложением или игрой объекты памяти. Часто используемые команды можно записывать и многократно воспроизводить в одном кадре или в нескольких кадрах с помощью наборов. Списки команд и очередь команд позволяют параллельно создавать списки команд в нескольких потоках ЦП. Практически вся работа равномерно распределяется по всем потокам ЦП, что позволяет раскрыть весь потенциал и всю мощь процессоров Intel® Core™ 4-го и 5-го поколений.
Direct3D 12 — значительный шаг в развитии технологий игр на ПК. Разработчики игр смогут работать «ближе к железу» за счет более компактного API и драйвера с меньшим количеством промежуточных уровней. За счет этого повышается эффективность и производительность. С помощью сотрудничества группа разработки D3D создала новый API и модель драйверов, предоставляющую разработчикам более широкие возможности управления, что позволяет создавать игры в полном соответствии с замыслом, с великолепной графикой и отличной производительностью.
Майкл Коппок (Michael Coppock) работает в корпорации Intel с 1994 года, он специализируется на производительности графики и игр для ПК. Он помогает компаниям, разрабатывающим игры, наиболее эффективно задействовать все возможности ГП и ЦП Intel. Занимаясь и программным обеспечением, и оборудованием, Майкл работал со множеством продуктов Intel, начиная с процессора 486DX4 Overdrive.
Примечания
ИНФОРМАЦИЯ В ДАННОМ ДОКУМЕНТЕ ПРИВЕДЕНА ТОЛЬКО В ОТНОШЕНИИ ПРОДУКТОВ INTEL. ДАННЫЙ ДОКУМЕНТ НЕ ПРЕДОСТАВЛЯЕТ ЯВНОЙ ИЛИ ПОДРАЗУМЕВАЕМОЙ ЛИЦЕНЗИИ, ЛИШЕНИЯ ПРАВА ВОЗРАЖЕНИЯ ИЛИ ИНЫХ ПРАВ НА ИНТЕЛЛЕКТУАЛЬНУЮ СОБСТВЕННОСТЬ. КРОМЕ СЛУЧАЕВ, УКАЗАННЫХ В УСЛОВИЯХ И ПРАВИЛАХ ПРОДАЖИ ТАКИХ ПРОДУКТОВ, INTEL НЕ НЕСЕТ НИКАКОЙ ОТВЕТСТВЕННОСТИ И ОТКАЗЫВАЕТСЯ ОТ ЯВНЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ ГАРАНТИЙ В ОТНОШЕНИИ ПРОДАЖИ И/ИЛИ ИСПОЛЬЗОВАНИЯ СВОИХ ПРОДУКТОВ, ВКЛЮЧАЯ ОТВЕТСТВЕННОСТЬ ИЛИ ГАРАНТИИ ОТНОСИТЕЛЬНО ИХ ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ, ОБЕСПЕЧЕНИЯ ПРИБЫЛИ ИЛИ НАРУШЕНИЯ КАКИХ-ЛИБО ПАТЕНТОВ, АВТОРСКИХ ПРАВ ИЛИ ИНЫХ ПРАВ НА ИНТЕЛЛЕКТУАЛЬНУЮ СОБСТВЕННОСТЬ.
КРОМЕ СЛУЧАЕВ, СОГЛАСОВАННЫХ INTEL В ПИСЬМЕННОЙ ФОРМЕ, ПРОДУКТЫ INTEL НЕ ПРЕДНАЗНАЧЕНЫ ДЛЯ ИСПОЛЬЗОВАНИЯ В СИТУАЦИЯХ, КОГДА ИХ НЕИСПРАВНОСТЬ МОЖЕТ ПРИВЕСТИ К ТРАВМАМ ИЛИ ЛЕТАЛЬНОМУ ИСХОДУ.
Корпорация Intel оставляет за собой право вносить изменения в технические характеристики и описания своих продуктов без предварительного уведомления. Проектировщики не должны полагаться на отсутствующие характеристики, а также характеристики с пометками «Зарезервировано» или «Не определено». Эти характеристики резервируются Intel для будущего использования, поэтому отсутствие конфликтов совместимости для них не гарантируется. Информация в данном документе может быть изменена без предварительного уведомления. Не используйте эту информацию в окончательном варианте дизайна.
Продукты, описанные в данном документе, могут содержать ошибки и неточности, из-за чего реальные характеристики продуктов могут отличаться от приведенных здесь. Уже выявленные ошибки могут быть предоставлены по запросу.
Перед размещением заказа получите последние версии спецификаций в региональном офисе продаж Intel или у местного дистрибьютора.
Копии документов с порядковым номером, ссылки на которые содержатся в этом документе, а также другую литературу Intel можно получить, позвонив по телефону 1-800-548-47-25 либо на сайте http://www.intel.com/design/literature.htm.
Intel, эмблема Intel, Intel Atom и Intel Core являются товарными знаками корпорации Intel в США и в других странах.
* Другие наименования и торговые марки могут быть собственностью третьих лиц.
Intel, эмблема Intel, Intel Atom и Intel Core являются товарными знаками корпорации Intel в США и в других странах.
* Другие наименования и торговые марки могут быть собственностью третьих лиц.
Компиляторы Intel могут не обеспечивать ту же степень оптимизации для других микропроцессоров (не корпорации Intel), даже если в них реализованы такие же возможности для оптимизации, как в микропроцессорах Intel. К ним относятся наборы команд SSE2®, SSE3 и SSSE3 и другие возможности для оптимизации. Корпорация Intel не гарантирует доступность, функциональность или эффективность какой-либо оптимизации на микропроцессорах других производителей. Микропроцессорная оптимизация, реализованная в этом продукте, предназначена только для использования с микропроцессорами Intel. Некоторые виды оптимизации, применяемые не только для микроархитектуры Intel, зарезервированы для микропроцессоров Intel. Ознакомьтесь с руководством пользователя и справочным руководством по соответствующему продукту для получения более подробной информации о конкретных наборах команд, которых касается данное уведомление.
Редакция уведомления № 20110804
ПРИМЕЧАНИЕ.В зависимости от содержимого могут потребоваться дополнительные уведомления и примечания. Как правило, они находятся в следующих местах, и их необходимо добавить в раздел «Уведомления» соответствующих документов.
Общие уведомления:уведомления, размещаемые во всех материалах, распространяемых и выпускаемых корпорацией Intel.
Сопутствующие технические уведомления:уведомления, которые следует добавлять в технические материалы Intel, описывающие внешний вид, пригодность или функциональность продукции Intel.
Технические примечания:примечания к материалам Intel, когда описываются преимущества или возможности технологий и программ.
Примечание для технических уведомлений: если все описываемые продукты (например, ACER ULV) обладают определенной функцией или поддерживают определенную технологию, то можно удалить заявление о требованиях в уведомлении. При наличии нескольких технических уведомлений можно объединить все заявления «фактическая производительность может различаться» в одно общее.
On March 2, 2015, during the Mobile World Congress in Barcelona, Spain, one of the announcements Intel made was to introduce the Intel® Atom™ x3 Processor Series, Intel’s first integrated communication platform. Formerly code named “SoFIA”, Intel® Atom™ x3 Processor Series is a low-cost SoC with 64-bit Intel Atom processor cores and integrated cellular baseband modem for smart or feature phones, phablets, and tablets. The SoC will be available in 4G LTE and 3G versions.
Intel® Atom™ x3 Processor Series provides a foundation for full-featured and cost-effective platforms with fast and seamless mobile experiences which meet today’s consumers’ expectations.
This blog will go through the high level features of the Intel® Atom™ x3 platform, especially the features which mobile app developers are interested in.
Please note that the processor series has not been officially released yet, information discussed in this blog post is subject to change without notice.
The Intel® Atom™ x3 Processor Series includes 3 versions (SKUs): the Intel® Atom™ x3-C3440 processor, the Intel® Atom™ x3-C3130 processor, and the Intel® Atom™ x3-C3230RK processor.
Architectures and Specifications
The Intel® Atom™ x3-C3440 processor includes a 64-bit quad-core Intel® Atom™ CPU and an integrated 4G LTE 5-band modem. The upgraded video can provide 1080p HD playback. The Mali T720 MP2 GPU supports Open GL ES 3.0 and DirectX 9.3. Figure 1 shows the Intel® Atom™ x3-C3440 processor high level box diagram.
Figure 1The Intel® Atom™ x3-C3440 processor high level block diagram
Besides the C3440 version which supports the 4G LTE technologies, the Intel® Atom™ x3-C3130 processor series also includes 2 SKUs which support 3G mobile technologies: the Intel® Atom™ x3-C3130 processor and the Intel® Atom™ x3-C3230RK processor, which provide low-cost options with performance.
The Intel® Atom™ x3-C3130 processor features a 64-bit dual-core Intel® Atom™ CPU and an integrated 3G modem. Figure 2 shows its high level box diagram.
Figure 2 The Intel® Atom™ x3-C3130 processor features a dual-core Intel Atom CPU and an integrated 3G modem.
The Intel® Atom™ x3-C3230RK processor, a collaboration between Intel and Rockchip*, includes a quad-core 64-bit Intel® Atom™ CPU and an integrated 3G modem. Figure 3 describes this SKU’s high level architecture.
Figure 3 The Intel® Atom™ x3-C3230RK processor includes a quad-core Intel Atom CPU and an integrated 3G modem. During Intel Developer Forum in Shenzhen, China on April 8, 2015, Intel and Rockchip* announced devices based on the Intel® Atom™ x3-C3230RK processor are expected in market later in Q2, 2015
On the graphics side, the Intel® Atom™ x3-C3130 processor includes a Mali* 400MP2 GPU, and the Intel® Atom™ x3-C3230RK processor includes a Mali* 450 MP4 GPU. Both processors support up to OpenGL ES 2.0.
As a summary, Figure 4 shows a comparison table for the 3 Intel® Atom™ x3 processor SKUs.
Figure 4 A comparison of the Intel® Atom™ x3 processor SKUs
Feature Highlights
High Performance SoC integration for great mobile user experiences
The Intel Atom x3 processor series features 64-bit Intel Atom quad-core or dual-core IA processors at up to 1.4GHz Bust Mode, a fast 4G LTE or 3G integrated cellular baseband modem, HD video and quality audio, GPU, and the ISP module. The ISP supports dual cameras, including an up to 5MP front facing camera and up to 13MP rear-facing camera.
Low-power RF and low-power video encoding / decoding result in a long battery life.
Fast 4G LTE and 3G communications
4G LTE and 3G Intel modems are interoperable with mobile network operators around the globe and support world-wide roaming. In the 4G LTE SKU, up to 14 LTE bands can be supported with the 5-mode LTE modem (2G, 3G, 4G LTE, FDD/TDD, and TD-SCDMA).
Connectivity
The Intel Atom x3 processor series supports a full range of connectivity capabilities which will keep the user always connected: Wi-Fi, Bluetooth, and GPS and GNSS. These capabilities enable various mobile use cases, from networking to location-based services.
Impressive graphics and audio
The GPU provides clear and responsive graphics for games, including supports for OpenGL ES 2.0 (the C3130 and C3230RK SKUs) and 3.0 (the C3440 SKU). The platform supports high quality audio HD video media playback and Miracast-based wireless display.
Value-added capacities
The dual SIM capability enables mobile subscriptions from 2 different service providers / carriers. As an option on the C3440 SKU, Near Field Communication (NFC) supports streamlined and secure tap-and-pay transactions.
Summary
In the above discussion, we can see the Intel® Atom™ x3 Processor Series integrates high performance Intel® Atom™ CPU cores and a fast communication modem on a single SoC silicon. It provides a quality foundation for entry and value tablets, phablets, and smartphones affordable for consumers around the world.
Air quality monitoring is an interesting topic to explore with the rises in pollution, allergy sensitivity, awareness of health & fitness, and technology innovation. The consumer marketplace has seen innovative products released bringing more awareness to air quality monitoring in the home. One such product is the smart scale. These smart scales monitor a variety of health related parameters and also the air quality. The air quality is sent to the cloud and an app can alert you to the changes in the air quality so you will know when an area needs ventilation with fresh air. Having an awareness of the air quality could allow for an improved quality of life. This article shows a method of exploring air quality monitoring by measuring carbon dioxide, volatile organic compounds (VOC), and dust levels using the Arduino* ecosystem and sending the data to a cloud service provider.
The Intel® Edison platform is a natural fit for starting a new prototype or migrating an existing one given its fast processor, large memory size, and integrated connectivity for WiFi and Bluetooth. The Arduino ecosystem provides a capable set of hardware and firmware libraries to experiment with using the Intel® Edison Compute Module and Intel® Edison Arduino Breakout Board.
To learn more about the Intel Edison platform, please see the link below:
Figure 1 shows the hardware component connections to the Intel® Edison Arduino Breakout Board. The system uses an RGB LED as a simple visual indication system for displaying the air quality.
To determine the total air quality of an area, three sensors are used:
1. An optical dust sensor is used to measure the dust in the area.
2. A gas sensor is used to measure the Volatile Organic Compounds (VOC) such as smoke.
3. A CO2 sensor is used to measure the carbon dioxide levels with an I2C interface.
In addition, a motion sensor is used for helping the system get the best representation of the total air quality in an area, by filtering out temporary increases in dust concentration caused by movement, and temporary increases in CO2 concentration caused by a person breathing close to the sensors.
When there is no motion detected, the firmware reads the air quality sensors, analyzes the sensor data, updates the visual indication system, and sends the air quality data to the cloud. The details of the system are further discussed in the Firmware section.
To learn more about the sensors, please see the data sheets at the links below:
It is important to note that at the time of this writing, the default I2C clock frequency on Intel® Edison is above 100kHZ which is outside the specification of the K-30 CO2 sensor. The K-30 CO2 sensor supports a maximum I2C clock frequency (SCL) of 100kHz. The Intel® Edison I2C clock frequency can be changed to 100kHZ following a few steps:
-Ensure that the latest Intel® Edison Yocto firmware image is installed:
The following code shows the includes, macros, and functions for the air quality system. Functions for Initialization, Main Loop, Reading Motion Sensor, Reading Air Quality Sensors, Analyzing Total Air Quality, Updating Visual Indication LED, and Sending Data to a Cloud Service Provider are discussed.
Main Loop: The main loop initializes the system, checks for motion, reads the air quality sensors, analyzes the total air quality, updates the indication LED, and sends the data to a cloud service.
void loop() {
// -- Init
int airQuality = 0;
int motion = 0;
int sensorAirQuality[3] = {0,0,0}; //0-Gas Sensor, 1-CO2 Sensor, 2-DustSensor
Serial.println("");
// -- Check for motion
motion = readMotionSensor();
if (motion == MOTION_NOT_DETECTED) {
// -- Read Air Quality Sensors
readAirQualitySensors(sensorAirQuality);
// -- Analyze Total Air Quality
airQuality = analyzeTotalAirQuality(sensorAirQuality[0],sensorAirQuality[1],sensorAirQuality[2]);
// -- Update Indication LED
updateIndicationLED(airQuality);
// -- Update Air Quality Value for Cloud Datastream
updateCloudDatastreamValue(CHANNEL_AIR_QUALITY_ID, airQuality);
// -- Send Data To Cloud Service
sendToCloudService();
}
}
Reading Motion Sensor: The motion sensor is read by sampling the sensor’s digital output pin. If motion is detected, the sensor output pin will go HIGH. The function attempts to filter glitches and returns whether motion was detected or not.
int readMotionSensor() {
// -- Init
int motionSensorValue = MOTION_NOT_DETECTED;
int motion = MOTION_NOT_DETECTED;
Serial.println("-Read Motion Sensor");
// -- Read Sensor
motionSensorValue = digitalRead(motionSensorPin);
// -- Analyze Value
if (motionSensorValue == MOTION_DETECTED) {
delay(MOTION_DELAY_TIME);
motionSensorValue = digitalRead(motionSensorPin);
if (motionSensorValue == MOTION_DETECTED) {
motion = MOTION_DETECTED;
Serial.println("--Motion Detected");
updateIndicationLED(AIR_QUALITY_UNKNOWN);
}
}
return motion;
}
Reading Air Quality Sensors: This function calls the individual gas, co2, and dust sensor functions. The function takes a pointer to integer array for storing the air quality results for each sensor.
Reading Gas Sensor: The gas sensor can detect gases such as NH3, NOx, alcohol, Benzene, and smoke. The gas sensor contains an analog voltage output that is proportional to the gas levels in the air. An A/D conversion is performed to read this sensor. The function reads the sensor, averages the readings, analyzes the sensor data, and returns the air quality for this sensor.
int readGasSensor() {
// -- Init
int airQuality = 0;
int gasSensorValue = 0;
// -- Read Sensor
for (int i=0; i < MAX_SENSOR_READINGS; i++) {
gasSensorValue += analogRead(gasSensorPin);
delay(SENSOR_READING_DELAY);
}
gasSensorValue /= MAX_SENSOR_READINGS; //Average the sensor readings
// -- Update Cloud Datastream
Serial.print("--gasSensorValue = ");
Serial.println(gasSensorValue);
updateCloudDatastreamValue(CHANNEL_GAS_SENSOR_ID, gasSensorValue);
// -- Analyze Value
if (gasSensorValue < GAS_SENSOR_OPTIMAL) {
airQuality = AIR_QUALITY_OPTIMAL;
}
else if (gasSensorValue < GAS_SENSOR_GOOD) {
airQuality = AIR_QUALITY_GOOD;
}
else {
airQuality = AIR_QUALITY_BAD;
}
return airQuality;
}
Reading Dust Sensor: The dust sensor contains an optical sensing system that is energized using a digital output pin. An A/D conversion is then performed to sample the sensor’s analog voltage output that is proportional to the dust in the air. This function reads the sensor, averages the readings, analyzes the sensor data, and returns the air quality for this sensor.
int readDustSensor() {
// -- Init
int airQuality = 0;
int dustSensorValue = 0;
// -- Read Sensor
for (int i=0; i < MAX_SENSOR_READINGS; i++) {
digitalWrite(dustSensorLEDPin,LOW); //Enable LED
delayMicroseconds(SAMPLE_DELAY);
dustSensorValue += analogRead(dustSensorPin);
delayMicroseconds(PULSEWIDTH_DELAY);
digitalWrite(dustSensorLEDPin,HIGH); //Disable LED
delayMicroseconds(PERIOD_DELAY);
delay(SENSOR_READING_DELAY);
}
dustSensorValue /= MAX_SENSOR_READINGS; //Average the sensor readings
// -- Update Cloud Datastream
Serial.print("--dustSensorValue = ");
Serial.println(dustSensorValue);
updateCloudDatastreamValue(CHANNEL_DUST_SENSOR_ID, dustSensorValue);
// -- Analyze Value
if (dustSensorValue < DUST_SENSOR_OPTIMAL) {
airQuality = AIR_QUALITY_OPTIMAL;
}
else if (dustSensorValue < DUST_SENSOR_GOOD) {
airQuality = AIR_QUALITY_GOOD;
}
else {
airQuality = AIR_QUALITY_BAD;
}
return airQuality;
}
Reading CO2 Sensor: The CO2 sensor returns a CO2 concentration level in parts per million (ppm). The CO2 sensor is read through the I2C interface. This function reads the sensor, averages the readings, analyzes the sensor data, and returns the air quality for this sensor.
int readCO2Sensor() {
// -- Init
int airQuality = 0;
int co2SensorValue = 0;
int tempValue=0;
int invalidCount=0;
// -- Read Sensor
for (int i=0; i < MAX_SENSOR_READINGS; i++) {
tempValue = readCO2(); // see http://cdn.shopify.com/s/files/1/0019/5952/files/Senseair-Arduino.pdf?1264294173 for this function
(tempValue == 0) ? invalidCount++ : co2SensorValue += tempValue;
delay(SENSOR_READING_DELAY);
}
if (invalidCount != MAX_SENSOR_READINGS) {
co2SensorValue /= (MAX_SENSOR_READINGS - invalidCount); //Average the sensor readings
}
// -- Update Cloud Datastream
Serial.print("--co2SensorValue = ");
Serial.println(co2SensorValue);
updateCloudDatastreamValue(CHANNEL_CO2_SENSOR_ID, co2SensorValue);
// -- Analyze Value
if (co2SensorValue < CO2_SENSOR_OPTIMAL) {
airQuality = AIR_QUALITY_OPTIMAL;
}
else if (co2SensorValue < CO2_SENSOR_GOOD) {
airQuality = AIR_QUALITY_GOOD;
}
else {
airQuality = AIR_QUALITY_BAD;
}
return airQuality;
}
Analyzing Total Air Quality: This function determines the total air quality for the area by analyzing the gas, co2, and dust air quality values passed to this function. The function returns the total air quality level for the area.
int analyzeTotalAirQuality(int gasAirQuality, int co2AirQuality, int dustAirQuality) {
int airQuality = 0;
Serial.println("-Analyze Total Air Quality");
if (gasAirQuality==AIR_QUALITY_BAD \
|| dustAirQuality==AIR_QUALITY_BAD \
|| co2AirQuality==AIR_QUALITY_BAD) {
Serial.println("--Air Quality Is BAD");
airQuality = AIR_QUALITY_BAD;
}
else if (gasAirQuality == AIR_QUALITY_OPTIMAL \
&& dustAirQuality == AIR_QUALITY_OPTIMAL \
&& co2AirQuality==AIR_QUALITY_OPTIMAL) {
Serial.println("--Air Quality Is OPTIMAL");
airQuality = AIR_QUALITY_OPTIMAL;
}
else {
Serial.println("--Air Quality Is Good");
airQuality = AIR_QUALITY_GOOD;
}
return airQuality;
}
Updating Visual Indication LED: This function updates the indication LED to the appropriate color for the air quality value that is passed to this function. The LED turns blue for optimal air quality levels, green for good air quality levels, and red for bad air quality levels. The LED turns magenta if motion is detected.
void updateIndicationLED(int airQuality) {
Serial.println("-Update Indication LED");
// --Turn off all colors
digitalWrite(redRGBLEDPin,LOW);
digitalWrite(greenRGBLEDPin,LOW);
digitalWrite(blueRGBLEDPin,LOW);
// --Update Indication LED
if (airQuality == AIR_QUALITY_UNKNOWN) {
digitalWrite(redRGBLEDPin,HIGH);
digitalWrite(greenRGBLEDPin,HIGH);
digitalWrite(blueRGBLEDPin,HIGH);
}
else if (airQuality == AIR_QUALITY_OPTIMAL) {
digitalWrite(blueRGBLEDPin, HIGH);
}
else if (airQuality == AIR_QUALITY_GOOD) {
digitalWrite(greenRGBLEDPin, HIGH);
}
else {
digitalWrite(redRGBLEDPin, HIGH);
}
}
Sending Data to a Cloud Service Provider:
To connect Intel® Edison to a WiFi network, please see the link below:
In this example, xively.com is used as the cloud service provider that the air quality data is sent to. Figure 2 shows an example feed with four channels. The channels are further discussed in the Functions section. Integration with xively.com requires the Http Client and Xively libraries added to the Arduino IDE. Please see the link below to learn more about xively.com, creating an account, Arduino tutorials, and library integration with the Arduino IDE.
Updating the data stream: This function is called to update the values for a xively.com channel datastream. The function is passed the channelID, and the datastream value. In this system as shown in Figure 2, four datastreams are used. The datastreams are updated with raw sensor data from the gas, co2, and dust sensor functions. In addition, a datastream is also updated in the main loop with the total air quality value.
void updateCloudDatastreamValue(int channelID, int value) {
// -- Update the Datastream Value
datastreams[channelID].setFloat(value);
}
Sending the Datastreams to Xively: This function performs a PUT operation to a xively.com feed. The function returns the status of successful or the error code. The main loop calls this function.
void sendToCloudService() {
int status=0;
Serial.println("-Send To Cloud Service”);
// -- Upload the Datastream to Xively
status = xivelyclient.put(feed, XIVELY_KEY);
// -- Verify Transaction
if (status == XIVELY_HTTP_SUCCESS) {
Serial.println("--HTTP OK");
}
else {
Serial.print("--ERROR: ");
Serial.println(status);
}
}
Summary:
Hope you enjoyed exploring air quality monitoring with the Intel Edison platform. Challenge yourself to add additional indication showing the status of each sensor, to add enhancements to the cloud service experience with alert triggers when the air quality changes, and also look for opportunities to integrate air quality monitoring with other systems.
Mike Rylee is a Software Engineer at Intel Corporation with a background in developing embedded systems and apps on Android*, Windows*, iOS*, and Mac*. He currently works on enabling for Android and the Internet of Things.
++This sample source code is released under the Intel Sample Source License
Notices No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document.
Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade.
This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule, specifications and roadmaps.
The products and services described may contain defects or errors known as errata which may cause deviations from published specifications. Current characterized errata are available on request.
Copies of documents which have an order number and are referenced in this document may be obtained by calling 1-800-548-4725 or by visiting www.intel.com/design/literature.htm.
Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.
*Other names and brands may be claimed as the property of others
The Unity Toolkit for Intel® RealSense™ technology is a standard Unity package. It contains all the DLLs and scripts that are needed to use the Intel RealSense SDK in a Unity game or application. The toolkit allows for ease of use by making scripting properties available in the Inspector without the need to do much of the boilerplate coding required in the previous version of the SDK, the Intel® Perceptual Computing SDK.
Purpose
The purpose of this tutorial is to show you how to use the Unity Toolkit’s SendMessage action. The SendMessage action allows you to call a function from another script whenever an Intel RealSense application event is triggered. This can be very useful when you want to use functionality from a different script than what is included in any of the action scripts.
What this Tutorial Covers
This tutorial will cover the steps needed to use the SendMessageAction action.
Assumptions
You must have read the sdktoolkit.pdf file, which is installed in the \RSSDK\doc folder. This document gives you a general idea how the Unity add-on works. This tutorial also assumes that you have some basic Unity skills such as importing an external package, adding a cube onto a scene, and applying a script to a game object.
Requirements
Unity Professional 4.1 or higher.
An Intel® RealSense™ 3D camera either embedded in a device or an external camera.
Let’s Begin
Import the Visual Studio* Plugin (Optional)
While this step is not required, I prefer to use Visual Studio as my IDE. If you would like to use this free plugin, you can download it here http://unityvs.com/.
Import the Unity* package for Intel® RealSense™ Technology
This package contains everything you need to set up and run Intel RealSense applications. The Unity package is located in the RSSDK\Framework\Unity folder. If you installed the Intel RealSense SDK in the default location, the RSSDK folder is in C:\Program Files (x86).
You can import the Unity Toolkit as you would any package. When doing so, you have the options to pick and choose what components you want to use. For the purpose of this tutorial, we will use the defaults and import everything.
As you can see in the following image, there are now several new items under the Assets folder.
Plugins and Plugins.Managed contain DLLs required for using the Intel RealSense SDK.
RSUnityToolkit folder contains all the scripts and assets for running the toolkit.
We won’t go into what all the folders are and what they contain here as that is out of the scope of this tutorial.
To make sure the SendMessageAction action works properly on a game object, you first need to drag and drop the SendMessageAction script onto the game object. Second, you need to create a new script that will contain a function that you want to be called whenever an Intel RealSense SDK action has been triggered.
Create a new folder, then create a new script
To keep things organized, I created a new folder to hold my custom script. Inside that folder, I created a new C# script called MyRotateScript. Inside this script I set up a function that will rotate the cube along the X axis at a set interval.
Any time this function is called, the cube is rotated 30 degrees on each axis.
Once you have saved your script, return to Unity and drag and drop this new script onto your cube game object. You won’t need to worry about editing this script in the Inspector. However, it is important that you add this script to your cube game object. The SendMessageScript relies on the fact that this script is attached to the same game object.
Add the SendMessageAction script
Grab the SendMessageAction script and drop it onto the cube.
Next choose the hand options. I have chosen Right hand. What this will do is that any time the camera sees my hand it will enable the Send Message Action.
One thing to note, the event fires only when the SDK detects your hand, and only one message is sent at a time. So, to see the cube rotate more than once, you must move your hand out of the camera’s view and bring it back.
Congratulations, at this point, everything is ready to go.
For all of you Java developers out there that have be searching for tools to help with your Intel® Active Management Technology solutions, your search is over. I’d like to introduce you to the Intel® WS-Management Java Client Library, available for download here.
This download contains source code for a library that can be used to make WS-Management calls to Intel® AMT devices. Also included are code samples that demonstrate how to use the library to interact with many AMT features and documentation on the library & samples.
Key Features
Allows Intel® AMT development in Java environments
100% pure Java (no JNI or native code wrapping)
Supports Digest and Kerberos authentication
Library is packaged in a single .jar file with JavaDocs
Supports JDK5 and newer (JDK6 required for native Kerberos support)
Getting Started
Just include IntelWsmanLib.jar in your class path and you can start calling the API. I wrote a few of the samples in the download using the NetBeans Integrated Development Environment. But you can use any Java capable environment as long as you include the jar file. Relevant objects are found in the intel.management.wsman Java package.
The library is WS-Management compliant and not AMT specific or even AMT version specific. There are no WSDL files, XSD files, or MOF files needed.
Writing Code
Here are some snippets to give you an idea of how the code looks.
First create a connection to a WS-Management Service as follows:
Then use the resulting connection object to perform WS-Transfer, WS-Enumeration, or WS-Eventing operations using either digest or Kerberos credentials.
As you are probably aware, we did two updates in the past couple of weeks. The March 30 update, build 1878, was a regular update to fix a number of open issues. This one today, build 1912, is to address some regressions with that update. Not what we had planned. From all of us in the Intel® XDK team, our apologies for having to give you another update so quickly. Fortunately, we do know exactly why and how the regressions happened and are correcting our development and testing processes appropriately.
So, what’s in this update?
Bug-fixes – that’s it. We fixed the Cordova* plugins regressions – selecting/unselecting the standard Cordova plugins and the intel.xdk plugins, a problem with selecting 3rd-party plugins, issues with ajax calls, and an issue with the Emulator giving a 404 error when it couldn’t find the index.html file. There was also a firewall proxy problem a few users reported on OS X* that we fixed. There were a few others, check the Release Notes to see if we fixed the problems you may have. If you don’t see it there, please make sure you visit our user forums.
Also, we continued to improve the secure, single sign-on support we released earlier in March, allowing your Intel XDK login to work with the user forums on the Intel Developer Zone. There were a few issues with non-English characters in user names that we resolved.
Cordova* changes coming!
Now, an important note about Apache* Cordova*. We are a big fan, supporter, and contributor to the Apache Cordova project – it is doing a great service to mobile app developers in helping get access to native functionality in a standard way. It also moves very fast, which is important given the rapidly changing nature of web development and web technologies. As a tools vendor for HTML5 and one which views Cordova support as central to the core value of the product – the Intel XDK’s cross-platform support – we have to move very quickly as well to ensure we can offer new Cordova functionality as soon as it is ready. It is challenging to stay up to date, but it is something we must do.
Cordova 5.0 is being released this Summer. It is a big change in that plugins will become node packages, adopting the NPM naming conventions, and the plugins registry will be going away as of this Fall. App developers will need to be aware of the changes to how to integrate plugins, and the new names; We intend to support 5.0 fully and will have releases in the Summer for it to continue to make it as easy as possible to add plugins into apps. We’ll also be looking at ways to preview apps with plugins (coming soon!). But, primarily, we want to make sure you are aware of the changes coming up so you can prepare. Please let us know if you have any questions. We’ll keep info on Cordova 5.0 on our user forums and website over the coming months.
Please provide us feedback on our User Forum as to what or how you would like to see us help the Cordova project and your app development.
We greatly appreciate your feedback and comments in the user forums and in private. Please keep them coming!
FLOPS means total floating point operations per second, which is used in High Performance Computing. In general, Intel(R) VTune(TM) Amplifier XE
only provides metric named Cycles Per Instruction (average CPI), that is to measure performance for general programs.
In this article, I use matrix1.c as an example and show what events will be used to calculate FLOPS in code for different platform.
First at all, I will use Intel(R) C++ compiler with different switchers to generate binary for legacy x87, SSE, AVX on Intel(R) Xeon
processor, vector instructions on Intel(R) Xeon Phi(TM) coprocessor, then use events to calculate FLOPS.
(I work on 2nd Generation Intel(R) Core(TM) Architecture, Sandy Bridge processor, CPUfrequency is 3.4 GHz, 64bit operation system)
(I also work on Intel Xeon Phi coprocessor, CPU frequency is 1.09GHz)
1. Use X87 OPS as traditional FP to generate legacy X87 instructions used, calculate FLOPS
Build:
gcc -g –mno-sse matrix1.c -o matrix1.x87
Run VTune:
amplxe-cl -collect-with runsa -knob event-config=FP_COMP_OPS_EXE.X87:sa=10000 -- ./matrix1.x87
There were 777,070,000 counts of SIMD_FP_256.PACKED_DOUBLE
Elapsed time of multiply() = 1,486,002,000 / 3,400,000,000 = 0.437s
FLOPS = 777,070,000 / 1,000,000 / 0.437 = 1778.19 Mflops
4. Use vector instructions by using Intel C++ compiler to build native program for Intel Xeon Phi coprocessor, calculate FLOPS
Build:
icc -g -fno-inline -mmic -O3 matrix1.c -o matrix1.MIC
FP operations of application will be processed via the vector processing unit (VPU), which provides data parallelism, VTune provides supported events:
VPU_DATA_READ
VPU_DATA_WRITE
There were (176+134)=300M counts of VPU_DAT_READ & VPU_DATA_WRITE
Elapsed time of multiply() = 2,152,000,000 / 1,090,000,000 = 1.974s
FLOPS = 300,000,000 / 1,000,000 / 1.974 = 151.97 Mflops
Please note that my example is a single thread app working on one core, and you may develop multithreaded app working on multiple cores of Intel Xeon Phi coprocessor.
I have unintentionally raised a large debate recently concerning the question if it is legal in C/C++ to use the &P->m_foo expression with P being a null pointer. The programmers' community divided into two camps. The first claimed with confidence that it wasn't legal while the others were as sure saying that it was. Both parties gave various arguments and links, and it occurred to me at some point that I had to make things clear. For that purpose, I contacted Microsoft MVP experts and Visual C++ Microsoft development team communicating through a closed mailing list. They helped me to prepare this article and now everyone interested is welcome to read it. For those who can't wait to learn the answer: That code is NOT correct.
It all started with an article about a Linux kernel's check by the PVS-Studio analyzer. But the issue doesn't have to do anything with the check itself. The point is that in that article I cited the following fragment from Linux' code:
static int podhd_try_init(struct usb_interface *interface,
struct usb_line6_podhd *podhd)
{
int err;
struct usb_line6 *line6 = &podhd->line6;
if ((interface == NULL) || (podhd == NULL))
return -ENODEV;
....
}
I called this code dangerous because I thought it to cause undefined behavior.
After that, I got a pile of emails and comments, readers objecting to that idea of mine, and even was almost about to give in to their convincing arguments. For instance, as proof of that code being correct they pointed out the implementation of the offsetof macro, typically looking like this:
#define offsetof(st, m) ((size_t)(&((st *)0)->m))
We deal with null pointer dereferencing here, but the code still works well. There were also some other emails reasoning that since there had been no access by null pointer, there was no problem.
Although I tend to be gullible, I still try to double-check any information I may doubt. I started investigating the subject and eventually wrote a small article: "Reflections on the Null Pointer Dereferencing Issue".
Everything suggested that I had been right: One cannot write code like that. But I didn't manage to provide convincing proof for my conclusions and cite the relevant excerpts from the standard.
After publishing that article, I again was bombarded by protesting emails, so I thought I should figure it all out once and for all. I addressed language experts with a question to find out their opinions. This article is a summary of their answers.
About C
The '&podhd->line6' expression is undefined behavior in the C language when 'podhd' is a null pointer.
The C99 standard says the following about the '&' address-of operator (6.5.3.2 "Address and indirection operators"):
The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.
The expression 'podhd->line6' is clearly not a function designator, the result of a [] or * operator. It is an lvalue expression. However, when the 'podhd' pointer is NULL, the expression does not designate an object since 6.3.2.3 "Pointers" says:
If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
When "an lvalue does not designate an object when it is evaluated, the behavior is undefined" (C99 6.3.2.1 "Lvalues, arrays, and function designators"):
An lvalue is an expression with an object type or an incomplete type other than void; if an lvalue does not designate an object when it is evaluated, the behavior is undefined.
So, the same idea in brief:
When -> was executed on the pointer, it evaluated to an lvalue where no object exists, and as a result the behavior is undefined.
About C++
In the C++ language, things are absolutely the same. The '&podhd->line6' expression is undefined behavior here when 'podhd' is a null pointer.
The discussion at WG21 (232. Is indirection through a null pointer undefined behavior?), to which I referred to in the previous article, brings in some confusion. The programmers participating in it insist that this expression is not undefined behavior. However, no one has found any clause in the C++ standard permitting the use of "poldh->line6" with "polhd" being a null pointer.
The "polhd" pointer fails the basic constraint (5.2.5/4, second bullet) that it must designate an object. No C++ object has nullptr as address.
Summing it all up
struct usb_line6 *line6 = &podhd->line6;
This code is incorrect in both C and C++ when the podhd pointer equals 0. If the pointer equals 0, undefined behavior occurs.
The program running well is pure luck. Undefined behavior may take different forms, including program execution in just the way the programmer expected. It's just one of the special cases of undefined behavior, and that's all.
You cannot write code like that. The pointer must be checked before being dereferenced.
Additional ideas and links
When considering the idiomatic implementation of the 'offsetof()' operator, one must take into account that a compiler implementation is permitted to use what would be non-portable techniques to implement its functionality. The fact that a compiler's library implementation uses the null pointer constant in its implementation of 'offsetof()' doesn't make it OK for user code to use '&podhd->line6' when 'podhd' is a null pointer.
GCC can / does optimize assuming no undefined behavior ever occurs, and would remove the null checks here -- the Kernel compiles with a bunch of switches to tell the compiler not to do this. As an example, the experts refer to the article "What Every C Programmer Should Know About Undefined Behavior #2/3".
You may also find it interesting that a similar use of a null pointer was involved in a kernel exploit with the TUN/TAP driver. See "Fun with NULL pointers". The major difference that might cause some people to think the similarity doesn't apply is that in the TUN/TAP driver bug the structure field that the null pointer accessed was explicitly taken as a value to initialize a variable instead of simply having the address of the field taken. However, as far as standard C goes, taking the address of the field through a null pointer is still undefined behavior.
Is there any case when writing &P->m_foo where P == nullptr is OK? Yes, for example when it is an argument of the sizeof operator: sizeof(&P->m_foo).
Acknowledgements
This article has become possible thanks to the experts whose competence I can see no reason to doubt. I want to thank the following people for helping me in writing it:
Michael Burr is a C/C++ enthusiast who specializes in systems level and embedded software including Windows services, networking, and device drivers. He can often be found on the StackOverflow community answering questions about C and C++ (and occasionally fielding the easier C# questions). He has 6 Microsoft MVP awards for Visual C++.
Billy O'Neal is a (mostly) C++ developer and contributor to StackOverflow. He is a Microsoft Software Development Engineer on the Trustworthy Computing Team. He has worked at several security related places previously, including Malware Bytes and PreEmptive Solutions.
Giovanni Dicanio is a computer programmer, specialized in Windows operating system development. Giovanni wrote computer programming articles on C++, OpenGL and other programming subjects on Italian computer magazines. He contributed code to some open-source projects as well. Giovanni likes helping people solving C and C++ programming problems on Microsoft MSDN forums and recently on StackOverflow. He has 8 Microsoft MVP awards for Visual C++.
Gabriel Dos Reis is a Principal Software Development Engineer at Microsoft. He is also a researcher and a longtime member of the C++ community. His research interests include programming tools for dependable software. Prior to joining Microsoft, he was Assistant Professor at Texas A&M University. Dr. Dos Reis was a recipient of the 2012 National Science Foundation CAREER award for his research in compilers for dependable computational mathematics and educational activities. He is a member of the C++ standardization committee.
I have studied numbers of errors caused by using the Copy-Paste method and can assure you that programmers most often tend to make mistakes in the last fragment of a homogeneous code block. I have never seen this phenomenon described in books on programming, so I decided to write about it myself. I called it the "last line effect".
Introduction
My name is Andrey Karpov and I do an unusual job - I analyze program code of various applications with the help of static analyzers and write descriptions of errors and defects I find. I do this for pragmatic and mercenary reasons because what I do is the way our company advertises its tools PVS-Studio and CppCat. The scheme is very simple. I find bugs. Then I describe them in an article. The article attracts our potential customers' attention. Profit. But today's article is not about the analyzers.
When carrying out analysis of various projects, I save bugs I find and the corresponding code fragments in a special database. By the way, anyone interested can take a look at this database. We convert it into a collection of html-pages and upload them to our website in the "Detected errors" section.
This database is unique indeed! It currently contains 1800 code fragments with errors and is waiting for programmers to study it and reveal certain regularity patterns among these errors. That may serve as a useful basis for many future researches, manuals and articles.
I have never carried out any special investigation of the material gathered by now. One pattern, however, is showing up so clearly that I decided to investigate it a bit deeper. You see, in my articles I have to write the phrase "note the last line" pretty often. It occurred to me that there had to be some reason behind it.
Last line effect
When writing program code, programmers often have to write a series of similar constructs. Typing the same code several times is boring and inefficient. That's why they use the Copy-Paste method: a code fragment is copied and pasted several times with further editing. Everyone knows what is bad about this method: you risk easily forgetting to change something in the pasted lines and thus giving birth to errors. Unfortunately, there is often no better alternative to be found.
Now let's speak of the pattern I discovered. I figured out that mistakes are most often made in the last pasted block of code.
Here is a simple and short example:
inline Vector3int32& operator+=(const Vector3int32& other) {
x += other.x;
y += other.y;
z += other.y;
return *this;
}
Note the line "z += other.y;". The programmer forgot to replace 'y' with 'z' in it.
You may think this is an artificial sample, but it is actually taken from a real application. Further in this article, I am going to convince you that this is a very frequent and common issue. This is what the "last line effect" looks like. Programmers most often make mistakes at the very end of a sequence of similar edits.
I heard somewhere that mountain-climbers often fall off at the last few dozens of meters of ascent. Not because they are tired; they are simply too joyful about almost reaching the top - they anticipate the sweet taste of victory, get less attentive, and make some fatal mistake. I guess something similar happens to programmers.
Now a few figures.
Having studied the bug database, I singled out 84 code fragments that I found to have been written through the Copy-Paste method. Out of them, 41 fragments contain mistakes somewhere in the middle of copied-and-pasted blocks. For example:
The length of the "THREADS=" string is 8 characters, not 6.
In other 43 cases, mistakes were found in the last copied code block.
Well, the number 43 looks just slightly bigger than 41. But keep in mind that there may be quite a lot of homogeneous blocks, so mistakes can be found in the first, second, fifth, or even tenth block. So we get a relatively smooth distribution of mistakes throughout blocks and a sharp peak at the end.
I accepted the number of homogeneous blocks to be 5 on the average.
So it appears that the first 4 blocks contain 41 mistakes distributed throughout them; that makes about 10 mistakes per block.
Figure 1. A rough diagram of mistake distribution in five homogeneous code blocks.
So what we get is the following pattern:
The probability of making a mistake in the last pasted block of code is 4 times higher than in any other block.
I don't draw any grand conclusions from that. It's just an interesting observation that may be useful to know about for practical reasons - you will stay alert when writing the last fragments of code.
Examples
Now I only have to convince the readers that it all is not my fancy, but a real tendency. To prove my words, I will show you some examples.
I won't cite all the examples, of course - only the simplest or most representative ones.
The programmer forgot to make 2 edits in the last line. Firstly, ">=" should be replaced with "<=; secondly, minus should be replaced with plus.
Qt
qreal x = ctx->callData->args[0].toNumber();
qreal y = ctx->callData->args[1].toNumber();
qreal w = ctx->callData->args[2].toNumber();
qreal h = ctx->callData->args[3].toNumber();
if (!qIsFinite(x) || !qIsFinite(y) ||
!qIsFinite(w) || !qIsFinite(w))
In the very last call of the function qIsFinite, the 'h' variable should have been used as an argument.
OpenSSL
if (!strncmp(vstart, "ASCII", 5))
arg->format = ASN1_GEN_FORMAT_ASCII;
else if (!strncmp(vstart, "UTF8", 4))
arg->format = ASN1_GEN_FORMAT_UTF8;
else if (!strncmp(vstart, "HEX", 3))
arg->format = ASN1_GEN_FORMAT_HEX;
else if (!strncmp(vstart, "BITLIST", 3))
arg->format = ASN1_GEN_FORMAT_BITLIST;
The length of the "BITLIST" string is 7, not 3 characters.
Let's stop here. I hope the examples I have demonstrated are more than enough.
Conclusion
From this article you have learned that with the Copy-Paste method making a mistake in the last pasted block of code is 4 times more probable than in any other fragment.
It has to do with the specifics of human psychology, not professional skills. I have shown you in this article that even highly-skilled developers of such projects as Clang or Qt tend to make mistakes of this kind.
I hope my observation will be useful for programmers and perhaps urge them to investigate our bug database. I believe it will help reveal many regularity patterns among errors and work out new recommendations for programmers.
This code recipe describes how to get, build, and use the Quantum ESPRESSO code that includes support for the Intel® Xeon Phi™ coprocessor with Intel® Many-Integrated Core (MIC) architecture. This recipe focuses on how to run this code using explicit offload.
Code Access
Quantum ESPRESSO is an integrated suite of open source computer codes for electronic-structure calculations and materials modeling at the nanoscale. It is based on density-functional theory, plane waves, and pseudo potentials. The Quantum ESPRESSO code is maintained by Quantum ESPRESSO Foundation and is available under the GPLv2 licensing agreement. The code supports the offload mode of operation of the Intel® Xeon® processor (referred to as ‘host’ in this document) with the Intel® Xeon Phi™ coprocessor (referred to as ‘coprocessor’ in this document) in a single node and in a cluster environment.
You can add “-xHost -ansi-alias” to CFLAGS as well as FFLAGS.
Build the Quantum ESPRESSO PW binary
$ make pw -j16
You should now have bin/pw.x
Change to the directory you cloned libxphi to and execute the build script. Make sure you do this in the shell you have the Intel compilers and Intel MPI library sourced.
$ cd libxphi
$ ./build-library.sh
You should now find two libraries: libxphi.so and libmkl_proxy.so
The build process is now complete.
Run Directions
A single Quantum ESPRESSO on a single node
The Quantum ESPRESSO binary compiled above initially has support for accelerated 3D FFT. Additionally, the library libxphi.so contains a number of linear algebra numerical routines invoked by Quantum ESPRESSO, particularly the numerically intensive ZGEMM BLAS3 routine for complex matrix-matrix multiplication. Instead of executing this routine via Intel® Math Kernel Library (Intel MKL), libxphi blocks the matrices and buffers them asynchronously to the card, where Intel MKL then executes the multiplication of the blocks and transfers the result back. When the Quantum ESPRESSO binary is created with the build instructions above, it will contain dynamic calls to the ZGEMM routine, which are usually satisfied by Intel MKL. To get offloaded ZGEMM in place, libxphi.so needs to be preloaded:
The last line executes the Quantum ESPRESSO binary pw.x with offloaded ZGEMM support. To make this easier, we provide a shell script that facilitates this preloading and just takes the binary and its arguments as input, so that the execution of an offloaded run would look like this:
In this case Quantum ESPRESSO will execute a single instance with OpenMP* threads (by default as many as you have cores) and offload FFT and ZGEMM to all the cores of the Intel Xeon Phi coprocessor.
Tuning the linear algebra offloading
To tune the offloading process, we need to understand the ZGEMM routine, which executes matrix-matrix multiplication
C=αA∙B+βC
where α and β are complex numbers and C, A and B are matrices of dimension MxN, MxK, and KxN, respectively. The library libxphi.so now blocks this matrix-matrix multiplication, so that the resulting block-matrix multiplication consists of smaller blocks that are continuously streamed to the coprocessor and back. The size of those blocks can be defined by three parameters, M, N, and K, where m x n, m x k, and k x n are the dimensions of the C-, A- and B-block, respectively. By default, libxphi will block the matrices in sizes of m=n=k =1024.You can play with these values to achieve better performance, depending on your workload size. We have found that making m and n somewhat larger (m=n=2048) and playing with the size of k (between 512 and 1024) can yield very good results.
Block size can be set via the environment variables QE_MIC_BLOCKSIZE_M, QE_MIC_BLOCKSIZE_N, and QE_MIC_BLOCKSIZE_K. For example:
An additional setting is required to avoid the offloading of small matrices, which might be more efficiently computed on the host instead of the coprocessor. With QE_MIC_OFFLOAD_THRESHOLD you can define the minimal number of floating point operations a matrix must have in order to get offloaded. The setting
$ export QE_MIC_OFFLOAD_THRESHOLD=20
achieves good results.
Partitioning the coprocessor
Partitioning the coprocessor leverages the advantages of multi-processing vs. multi-threading. It is somewhat similar to running Message Passing Interface (MPI) ranks on the coprocessor (a.k.a. symmetric usage model) although the MPI ranks are only on the host. Varying the number of ranks on the host can be used to partition each coprocessor into independent sets of threads. The vehicle to achieve independent thread-partitions is given by the KMP_PLACE_THREADS environment variable. In addition, using the environment variable OFFLOAD_DEVICES utilizes multiple coprocessors within the same system. Of course there is nothing wrong with using OpenMP instead of this proposed method; however, we found that portioning the coprocessor unlocks more performance–this is simply trading implicit locks at the end of parallel regions against absolutely independent executions. To ease the tuning process, a script is provided that generates the appropriate “mpirun”-command line.
$ ~/mpirun/mpirun.sh -h -n: list of comma separated node names -p: number of processes per socket (host) -q: number of processes per mic (native) -s: number of sockets per node -d: number of devices per node -e: number of CPU cores per socket -t: number of CPU threads per core -m: number of MIC cores per device -r: number of MIC cores reserved -u: number of MIC threads per core -a: affinity (CPU) e.g., compact -b: affinity (MIC) e.g., balanced -c: schedule, e.g., dynamic -0: executable (rank-0) -x: executable (host) -y: executable (mic) -z: prefixed mic name -i: inputfile (<) -w: wrapper -v: dryrun
The script “mpirun.sh” is actually inspecting the system hardware in order to provide defaults for all of the above arguments. The script then launches “mpirun.py,” which actually builds and launches the command line for “mpirun.” This initial inspection, for example, avoids using multiple host sockets in case there is only one coprocessor attached to the system (avoids performing data transfers to a “remote” coprocessor). Any default provided by the launcher script “mpirun.sh” can be overridden at the command line (while still being able to leverage all other default settings). Please note that the script also supports symmetric execution (“-y”, etc.), which is discussed here.
Here is an example of running QE with four partitions on each of the coprocessor(s):
Any argument passed at the end of the command line is simply forwarded to the next underlying mechanism if not consumed by option processing. If you need to pass arguments to the executable using “<”, you can use the script’s “-i” option; otherwise, options for the executable can be simply appended to the above command line.
The number of ranks per host-socket (“-p”) is not only dividing the number of cores per host-processor but also dividing each coprocessor’s number of cores. Therefore some ratios produce some remaining unused cores. On the other hand, the coprocessor usually comes with more cores than cores in a single host socket/processor; therefore, it is likely acceptable and anyways a subject of tuning the number of partitions.
Performance
Image may be NSFW. Clik here to view. Figure 1: Performance of Quantum Espresso executing the GRIR443 benchmark on 16 Xeon E5-2697v2 and 16 Xeon Phi 7120A. Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark* and MobileMark*, are measured using specific computer systems, components, software, operations, and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products.
Authors of PVS-Studio static code analyzers offer programmers to test their sight and to try finding errors in C/C++ code fragments.
Code analyzers work tirelessly and are able to find many bugs that can be difficult to notice. We chose some code fragments in which we had founded some errors using PVS-Studio.
Quiz is not intended to check C++ language knowledge. There are many quality and interesting tests. For instance, we would recommend this C++ Quiz then. In our case, we made our test just for fun.
We quite frequently hear an opinion that code analyzers are pointless tools. It is possible to find misplaced parenthesis or comma in five seconds. However, analyzer would not find difficult logical errors. Therefore, this tool could be useful only for students.
We decided to troll these people. There is a time limit in tests. We ask them to find an error in five seconds. Well, OK, not in five seconds, but in a minute. Fifteen randomly selected problems would be shown. Every solved problem worth one point, but only if user provided the answer in one minute.
We want to stress that we are not talking about syntax errors. We found all these code fragments in open-source projects that compiles flawlessly. Let us explain on a pair of examples how to point out the correct answer.
The bug here is highlighted with red color. Of course, there would be no such emphasizing in a quiz problem.
Programmer accidently made a misprint and wrote index 3 instead of index 2. Mouse cursor movement would highlight fragments of code, such as words and numbers. You should point the cursor into number 3 and press left mouse button.
This would be the correct answer.
Second example. It is not always possible to point out the error exactly.
Buffer size should be compared with number 48. An excess sizeof operator was put there by accident. In result, buffer size is compared with size of int type.
At my opinion, an error there is in sizeof operator, and it is required to point it out to score a correct answer. However, without knowledge about the whole text, it is possible to think this way. Sizeof operator should have evaluated the size of some buffer, but accidently evaluates the value of the macro. The error is in SSL3_MASTER_SECRET_LENGTH usage.
In this case, the answer will be scored no matter what you choose: sizeof or SSL3_MASTER_SECRET_LENGTH.
Test does not support mobile devices. It is very easy to miss with finger. We are working on new version of tests with better mobile devices support, new problems to solve etc. However, it is not implemented yet.
Over the past several years, the Internet of Things (IOT) has gained increasing attention from developers and consumers alike. You can now control almost everything in your home from your AC to your lights with a simple click of an app on your phone. In this article we make a plant monitor using the Intel® Edison board that autonomously waters a plant when the moisture level is too low and gives you the option to water on your command. Now you never have to worry about forgetting to water your plants, and your plants will be watered even when you’re out of town. You can follow this project on Twitter*: Intel Edison @PlantMonitor. We use the Arduino* IDE to write our sketch program to control the Intel Edison board. The data and other variables are sent to ThingSpeak* for data visualizations and tweeting. Links to all the necessary external libraries can be found at the end of this article. For more information on the Intel Edison board and Arduino IDE downloads: https://www-ssl.intel.com/content/www/us/en/do-it-yourself/edison.html.
The main component of our plant monitor is the moisture sensor; we use the Sunkee* soil hygrometer detection module. It measures the resistance between the two prongs in the soil to determine moisture content. We record the max and min of the sensor in the setup() to map the plant value to a percentage in the main loop(); below they are currently set to initial dummy values. The max value is the reading of air when the resistance is the greatest and the min value is when the sensor is completely submerged in water when resistance is the least. The value comes over as an analog input and we power it when needed using one of the digital pins to reduce the effects of electrolysis. We also set a percentage threshold for the plant to determine if it is overwatered, under-watered, or just right, and from those values trigger watering the plant when needed.
// Analog pin which we're monitoring
const int soilSensorPin = A0;
const int soilSensorPower = 7;
// soil variables:
int soil=0;
int soilSensorValue = 0; // the sensor value
int soilSensorMin = 1000; // min/wet sensor value
int soilSensorMax = 0; // max/dry sensor value
int overWaterThreshold=75;
int underWaterThreshold=50;
Code Example 1: Globals for the moiture sensor**
2.Light Sensor
As an added feature we also record the light level of the plant. Maybe the sunny spot it’s in only lasts a few hours but it needs sun all day long, so you know to move it to another spot. The Adafruit* TSL2561 high dynamic range digital light sensor is a sophisticated sensor that provides a much more accurate read than a photocell. It uses an I2C clock and an I2C data pin to communicate with the board. It requires two external libraries, the Adafruit Sensor library and the Adafruit TSL2561 library. You can learn more about it here: https://learn.adafruit.com/tsl2561/use
//for light and 8x8 bicolor LED
#include <Wire.h>
//for light sensor
#include <Adafruit_Sensor.h>
#include <Adafruit_TSL2561_U.h>
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345);
void configureSensor(void){
tsl.enableAutoGain(true); /* Auto-gain ... switches automatically between 1x and 16x */
/* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */
tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS); /* fast but low resolution */
}
Code Example 2: Globals for the light sensor**
3.LED Pixel Matrix
As a visual indicator of the plant status, we added an 8x8 Adafruit BiColor LED square pixel matrix with an I2C backpack to the plant monitor. It requires the same I2C pins as the light sensor, which is fine as I2C can communicate with multiple devices on the same bus. We can easily print words to the matrix, but to print our own shapes, we must define a binary 8x8 matrix so it knows which LEDs to turn on and off. When we write the shape to the matrix we also choose the color to indicate the status being bad or good. Below we defined a custom shape for the number ten as the default text version looks oddly small compared to numbers one through nine, a Wi-Fi signal to indicate the board cannot connect to ThingSpeak, a leaf for the plant status, and a question mark for when the data cannot be parsed from ThingSpeak. You can find more information about the LED matrix and the two external required libraries: https://learn.adafruit.com/adafruit-led-backpack/bi-color-8x8-matrix
Also from Adafruit, a 12V peristaltic liquid pump with silicone tubing is used to water the plant when the moisture level gets too low. The advantage of a peristaltic pump is that it won’t be damaged if there is no more liquid to draw from the reservoir. Adding an eTape* Liquid Level Sensor to the water reservoir itself to alert the user if that water level is low would be a good addition to this project. The pump requires its own separate 12V power supply, and as the board can only put out a max of 5V, the power supply is controlled by a relay switch that is in turn controlled by a pin on the board.
// Pin to drive the relay attached to the pump; high is off, low is on
int pump=2;
It wouldn’t be an IOT device without the Internet! So we will use a website that supports real time data collection and visualizations. For this project we use ThingSpeak which is currently offering its services for free and also tweets your project’s status (https://thingspeak.com/ ), but many other providers are available to choose from as well.
Below are the global variables that ThingSpeak needs, the most important being the thingSpeakChannel number and the writeAPIKey that will allow you to post to your feed. We also use the ArduinoJson Parser library to parse the ThingSpeak reads.
//for ThingSpeak
#include <WiFi.h>
#include <JsonParser.h>
// ThingSpeak Settings
WiFiClient clientWifi;
char thingSpeakAddress[] = "api.thingspeak.com";
String thingSpeakChannel = "24066";
int TSF_moisture_sensor;
int TSF_light_sensor;
int TSF_raw_moisture_data;
int TSF_raw_moisture_wettest_data; //min
int TSF_raw_moisture_driest_data; //max
int TSF_use_server_moisture_calibration;
int TSF_manual_water;
String writeAPIKey = "1KPP2Y48IZMDPISB";
// ThingSpeak Variable Setup
int failedCounter = 0;
int useServerMoistureValues= 0;
int initialSetup =0;
String twitterTweet= "";
Code Example 5: Globals for ThingSpeak**
Below is the method to read from your specified ThingSpeak feed. To fetch the data from your channel make sure that “Make Public?” is checked in your Channel Settings on the ThingSpeak website. If we have any issues connecting to the service, we change the LED to a red Wi-Fi signal for the user to see.
To update ThingSpeak, we send our string of data for it to parse and add to our graphs as well as our tweet. The composition of the data string will be discussed later on. To update Twitter make sure that the desired Twitter account is linked into ThingTweet in the apps section of ThingSpeak. If there are any issues connecting, we again set the LED matrix to the red Wi-Fi signal.
ThingSpeak then graphs each field of your data in a separate chart. In the image below you can see the moisture percentage slowly decreasing and the raw moisture data slowly increasing as resistance increases between the probes. We can also see that the light in the room came on at around 6:30am that day.
The first main part of the Arduino sketch is the setup loop that runs once on start up. It is here that we setup everything we need for our moisture sensor. First is the setup of the pump pin and then turning it off in the code to make sure the pump is not pumping water.
void setup() {
Serial.begin(9600);
pinMode(pump, OUTPUT);
//turn the relay off
digitalWrite(pump, HIGH);
Code Example 8: setup() method and pump initialization**
Then we initialize the LED matrix with the address that it is using.
//init the LED matrix
matrix.begin(0x70); // pass in the address
Code Example 9: setup() method and LED initialization**
Next we set up the calibration of our plant monitor. The raw data that comes over from the moisture sensor has no meaning without a baseline high and low to compare it to. Below we show a green leaf on start-up and then print out “Calibrate Me!” and count down from ten for the user to calibrate the moisture sensor. The best way to calibrate it is to transition the sensor from being in air to submerged in a cup of water during those ten seconds and then stick it into the plant when done. If the values the user wants to use are already in ThingSpeak, then those are taken into account during the loop() part of the sketch.
Serial.println("Starting plant monitor...");
Serial.println();
matrix.clear();
matrix.drawBitmap(0, 0, leaf_bmp, 8, 8, LED_GREEN);
matrix.writeDisplay();
delay(3000);
Serial.println("Soil moisture sensor");
// add a calibration setup
// calibrate during the first ten seconds
Serial.println("Please calibrate your moisture sensor in the next 10 seconds");
matrix.setTextWrap(false); // we dont want text to wrap so it scrolls nicely
matrix.setTextSize(1);
matrix.setTextColor(LED_GREEN);
for (int8_t x=7; x>=-94; x--) {
matrix.clear();
matrix.setCursor(x,0);
matrix.print("Calibrate Me!");
matrix.writeDisplay();
delay(150);
}
int offset=1;
for(int i=10; i>-1;i--){
soilSensorValue = analogRead(soilSensorPin);
// record the maximum sensor value
if (soilSensorValue > soilSensorMax) {
soilSensorMax = soilSensorValue;
}
// record the minimum sensor value
if (soilSensorValue < soilSensorMin) {
soilSensorMin = soilSensorValue;
}
matrix.setTextWrap(true);
matrix.setTextSize(1);
matrix.setTextColor(LED_GREEN);
matrix.clear();
if(i<10){
matrix.setCursor(0,0);
matrix.print(i);
}
else{
switch(i){
case 10:
//the standard 10 looks odd, so we created our own
matrix.drawBitmap(0, 0, ten_bmp, 8, 8, LED_GREEN);
break;
}
}
matrix.writeDisplay();
delay(1000);
}
matrix.clear();
Code Example 10: setup() method and moisture sensor calibration**
And finally we configure our light sensor set-up and delay an additional amount of time to ensure the user has enough time to place the probe in the soil.
/* Initialise the light sensor */
if(!tsl.begin())
{
/* There was a problem detecting the ADXL345 ... check your connections */
Serial.print("Ooops, no TSL2561 detected ... Check your wiring or I2C ADDR!");
while(1);
}
/* Setup the sensor gain and integration time */
configureSensor();
}
//in case the sensor is still in the water for calibration
delay(3000);
}
Code Example 11: setup() method and light sensor initialization**
Tip
If you encounter a sketch transfer incomplete error with a message of ‘Retry 0: Got ZCAN’, it is because your logs have filled it up with entries, leaving no more space in memory. You can either expand the memory of your board with an sdcard, or login as root over a serial connection and run ‘rm –rf /var/log/journal/*’ to delete all the logs.
7.Sketch Loop()
The loop is the part of the sketch that repeats over and over, collecting data and then submitting it to ThingSpeak. It begins by reading the data from ThingSpeak using an HTTP GET call that returns a string of data in JSON format. To parse this data into local variables to see if any of the user command variables have been set, we use the ArduinoJson library to load it into a hashTable from which we can easily obtain the individual values as strings. If there is a parse failure, we assume the first failure is due to the fact that there is no data there yet and proceed forward with the sketch to upload values. If the failures persist, then we wait a while giving the issue time to clear up and then restart the loop from the beginning.
void loop() {
if (clientWifi.connected()){
clientWifi.stop();
}
if(!clientWifi.connected()){
subscribeToThingSpeak(thingSpeakChannel);
}
String response= "";
char charIn;
if (clientWifi.available() > 0){
do {
charIn = clientWifi.read(); // read a char from the buffer
response += charIn; // append that char to the string response
}
while (clientWifi.available() > 0);
Serial.print("Reading ThingSpeak: ");
Serial.println(response);
Serial.println();
int len= response.length()+1;
char myString[len];
response.toCharArray(myString,len);
JsonParser<32> parser;
JsonHashTable hashTable = parser.parseHashTable(myString);
if (!hashTable.success()){
Serial.println("JsonParser.parseHashTable() failed");
initialSetup++;
if(initialSetup>1){
matrix.clear();
matrix.drawBitmap(0, 0, question_bmp, 8, 8, LED_RED);
matrix.writeDisplay();
delay(30000);
return;
}
}
else{
TSF_moisture_sensor = atoi(hashTable.getString("field1"));
TSF_light_sensor = atoi(hashTable.getString("field2"));
TSF_raw_moisture_data = atoi(hashTable.getString("field3"));
TSF_raw_moisture_wettest_data = atoi(hashTable.getString("field4"));
TSF_raw_moisture_driest_data= atoi(hashTable.getString("field5"));
TSF_use_server_moisture_calibration = atoi(hashTable.getString("field6"));
TSF_manual_water = atoi(hashTable.getString("field7"));
Code Example 12: loop() method and ThingSpeak data fetching**
If the user wants to input their own calibration values, then we check the TSF_use_server_moisture_calibration variable and set our local max and min to use the server values instead. Additionally, so the user doesn’t have to re-calibrate the sensor every time and instead keep the probe in the soil continuously, we check that the difference between the max and min is not great enough to indicate a calibration was done and proceed to use the server values. To input data into ThingSpeak outside of the sketch requires just a simple HTTP call to the ThingSpeak website using your writeAPIKey and the field numbers and values. For example, to input new calibration values and use them would be: https://api.thingspeak.com/update?key=1KPP2Y48IZMDPISB&field4=479&field5=1015&field6=1. One thing to note is that any value not included in this call will be changed to null, hence the sketch check for values that exactly equal one or are not null to prevent any errors from null data entry.
Code Example 13: loop() method and moisture sensor values set-up**
After we have our finalized max and min values we can get the soil reading by setting the pin HIGH to power the sensor before turning it off again and then mapping the value to an easily readable percentage. Based on the global threshold values, the sketch reports the status of the plant with an appropriately colored leaf on the LED matrix, compose the tweet string, and if necessary turn on the pump to water the plant.
digitalWrite(soilSensorPower, HIGH);
delay(2000);
soilSensorValue = analogRead(soilSensorPin);
digitalWrite(soilSensorPower, LOW);
soilSensorValue = constrain(soilSensorValue, soilSensorMin, soilSensorMax);
//map the value to a percentage
soil = map(soilSensorValue, soilSensorMin, soilSensorMax, 100, 0);
if (soil>=overWaterThreshold){
Serial.println("Water Level Too High");
matrix.clear();
matrix.drawBitmap(0, 0, leaf_bmp, 8, 8, LED_YELLOW);
matrix.writeDisplay();
// Update Twitter via ThingTweet
twitterTweet="&twitter=PlantMonitor&tweet=Plant water level is too high: "+ String(soil) + "%25.";
}
else{
if (soil <overWaterThreshold && soil>underWaterThreshold){
Serial.println("Water Level Good");
matrix.clear();
matrix.drawBitmap(0, 0, leaf_bmp, 8, 8, LED_GREEN);
matrix.writeDisplay();
twitterTweet="&twitter=PlantMonitor&tweet=Plant water level is good: "+ String(soil) + "%25.";
}
else{
Serial.println("Water Level Bad");
matrix.clear();
matrix.drawBitmap(0, 0, leaf_bmp, 8, 8, LED_RED);
matrix.writeDisplay();
twitterTweet="Plant water level is too low: "+ String(soil) + "%25. Watering now. ";
digitalWrite(pump, LOW);
delay(30000); // set how long you want the motor to run... 1000 = aprox 1ml
digitalWrite(pump, HIGH);
}
}
Code Example 14: loop() method and moisture level settings**
If a user wants to remotely trigger watering of their plant, they can do so by setting the TSF_manual_water variable to one (https://api.thingspeak.com/update?key=1KPP2Y48IZMDPISB&field7=1).
if(TSF_manual_water==1){
digitalWrite(pump, LOW);
delay(10000); // set how long you want the motor to run... 1000 = aprox 1ml
digitalWrite(pump, HIGH);
TSF_manual_water=0;
}
Code Example 15: loop() method and manual watering**
Next we collect the light measurement.
/* Get a new light sensor event */
sensors_event_t event;
tsl.getEvent(&event);
/* Display the results (light is measured in lux) */
if (!event.light) {
/* If event.light = 0 lux the sensor is probably saturated
and no reliable data could be generated! */
Serial.println("Sensor overload");
}
Code Example 16: loop() method and light sensor data collection**
And then it is time to submit all the data to ThingSpeak and tweet our plant’s status. The data string has every numbered field with its value and ends with our twitter handle and our tweet string.
Code Example 17: loop() method and ThingSpeak data submission**
At the very end of the loop we put a delay of one hour. The moisture level of the plant doesn’t need to be checked often, and the delay gives the water more time to disperse and settle between watering’s.
Now we have built our Edison plant monitor to water our plant for us. You can experiment with your own threshold levels and setting how much to water your plant. I used a small Nephthytis house plant for this project so your plants needs may be different.
Whitney Foster is a software engineer at Intel in the Software Solutions Group working on scale enabling projects for Android applications and IOT.
Notices
No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document.
Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade.
This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule, specifications and roadmaps.
The products and services described may contain defects or errors known as errata which may cause deviations from published specifications. Current characterized errata are available on request.
Copies of documents which have an order number and are referenced in this document may be obtained by calling 1-800-548-4725 or by visiting www.intel.com/design/literature.htm.
Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.
*Other names and brands may be claimed as the property of others
**This sample source code is released under the Intel Sample Source Code License AgreementLike SubscribeAdd new commentFlag as spam .
Photo opp - me and the Javascript Rhino we all love! :)
Last week I attended Fluent Conf for the second time. It’s great to see the continuing trend of the Web + IoT and other hardware + Javascript goodness.
Image may be NSFW. Clik here to view.
Though the main focus of this conference is on HTML/CSS/JS, quite a few of the talks this year were also focused connecting web technologies with other real world experiences. Robotics, WebVR, Wearables, etc. were all topics of presentation and there were some great talks on these subjects.
Image may be NSFW. Clik here to view.
A lot of companies have adopted ES6 already and using Transpilers for browser compatibility. Almost everyone is giving a talk on React; it’s amazing how many companies are using it since React was announced last year. Here is Brian Holt giving a talk on using both ES6 and React together.
Image may be NSFW. Clik here to view.
Javascript robotics! A few hardware vendor/projects were at the exhibit hall at Fluent. It’s great to see worlds collide and amazing things come out of it. Tessel is also back this year with their Tessel 2 board!
Image may be NSFW. Clik here to view.
Image may be NSFW. Clik here to view.
I have been playing around with MQTT for a while and it’s great to see that now it has almost become the defacto protocol for IoT applications. I had the pleasure of attending a great talk about IoT and voice enabled applications using IBM’s BlueMix and Watson Text-To-Speech technology. The demo showed a Sphereo being controlled by voice!
Image may be NSFW. Clik here to view.
I got to see the demo up close and had a quick chat with Stewart Nickolas, a distinguished engineer at IBM!
Image may be NSFW. Clik here to view.
Writing apps for wearables using Javascript...what??!?! Actually, Pebble has already made that possible with their PebbleJS and PebbleKit, but it’s interesting to see that it’s actually possible on other platforms too. Dan Gallo gave a great talk on using JS on different wearable platforms.
Image may be NSFW. Clik here to view.
I also attended a talk by Ron Evans and got this awesome Cylon.JS sticker after speaking to him in person. He put together a great talk on “Full Stack Robotics”!
Image may be NSFW. Clik here to view.
I love my O’Reilly animal stickers :)
I had a great time at Fluent this year (I do every year) and it’s quite interesting to see the convergence of web technologies/developers and hardware. I can’t wait to see what they have in store for next year at Fluent!
Intel® Integrated Native Developer Experience (Intel® INDE) is a one stop productivity suite to create native applications targeting the Android* and Microsoft* Windows* platforms. This guide will help you get started with developing high performance native applications using the various features of Intel INDE.
Developing Windows* Applications
Intel INDE can be used to develop Windows* applications using a Microsoft Windows* host system. For more help on using Intel INDE with this platform, refer to this guide:
Intel INDE can be used to develop Android* applications for mobile platforms using either a Microsoft Windows* host system or an Apple OS X* host system. As the tools available on each development platform are different, refer to the Getting Started Guide for your development system:
Start developing apps with Intel INDE! See the Intel INDE Home Page for information about the different versions of the product, how to purchase it, and how to get support for the product.