J.--P Attal
email: elsa.vennat@ecp.fr

A natural biomimetic porous medium mimicking hypomineralized enamel

Keywords: hypomineralized enamel, biomimetics, minimally invasive dentistry, mineral porous medium, hydroxyapatite, deproteinized dentin, porosimetry, thermogravimetry, nanoindentation, SEM, tissue engineering, porous scaffold

In order to evaluate the clinical impact of low viscosity resin infiltration in hypomineralized enamel, it is necessary to obtain a biomimetic porous substrate capable of mimicking enamel. The specifications for the biomimetic porous medium are defined using the literature data on hypomineralized enamel. Based on these specifications, we propose to use deproteinized dentin, the latter being deproteinized by heat treatment.

Thermogravimetry Analysis (TGA), Field Emission Scanning Electron Microscopy (FESEM) observations, Mercury Intrusion Porosimetry (MIP) tests and nanoindentation are performed on the deproteinized dentin tissue. Heat treatment is shown to be an effective and reproducible method for removing organic fluids and protein residues in dentin.

Deproteinizing dentin also enables forming nanovoids by eliminating its organic matrix. The interconnected open nanoporosities (porosities of less than 100nm) created at 600°C are distributed between 14 nm and 32 nm and the total porosity is 39% (including 36% due to nanoporosities). At 800°C, they are distributed between 60 nm and 100 nm and total porosity is 37% (including 33% arising from the nanoporosities). The hydroxyapatite crystal structure is transformed less at 600°C, so this temperature should be preferred.

Besides providing new understanding of the dentin tissue itself, this study led to characterizing a porous medium made of natural apatite, and proposing and validating its use as a porous medium mimicking hypomineralized enamel. The next logical step of this study is the characterization of resin infiltration in this medium and its mechanical reinforcement.

Introduction

Enamel is the most mineralized tissue of the organism with a mineral component evaluated at 96% by weight and 87% by volume. However, the mineral component may be affected by certain pathologies. In the case of hypomineralization, the rate of mineralization decreases without loss of volume. In this case the enamel is called hypomineralized enamel.

In hypomineralized enamel, the mineral part made up of hydroxyapatite crystals (Ca 10 (PO 4 ) 6 (OH) 2 ) is smaller than in sound enamel, reaching only 50 to 80% by volume. At the macroscopic scale, hypomineralized enamel becomes opaque, leaving unsightly white spots.

At the microscopic scale, it presents interconnected porosity (20 to 200 nm) due to ultrastructure alteration (enlargement of the inter--prismatic sheath [1--2] and localized acid dissolution of intra--prismatic crystals [START_REF] Palamara | Ultrastructure of the intact surface zone of white spot and brown spot carious lesions in human enamel[END_REF]). From the mechanical standpoint, hypomineralized enamel is weaker than sound enamel because mechanical properties like hardness and elastic modulus are strongly correlated with its degree of mineralization [4--6]. Its hardness and elastic modulus are around 0.5±0.3 GPa and 14.5±7.5 GPa [START_REF] Mahoney | Mechanical properties and microstructure of hypomineralised enamel of permanent teeth[END_REF], respectively, versus 3GPa to 6GPa and 70GPa to 115GPa for sound enamel [7--11].

The objective of the Resin Infiltration Technique, a new treatment fully compatible with the concept of Minimally Invasive Dentistry [START_REF] Simonsen | The preventive resin restoration : a minimally invasive nonmetallic restoration[END_REF], is to mask the white lesions of hypomineralized enamel. This treatment is based on the use of a photopolymerizable low viscosity resin with a refractive index close to that of sound enamel [13--14]. Besides remaining a minimally invasive procedure [15--17], the treatment masks white opacity [18--19] and reinforces the mechanical characteristics [START_REF] Torres | Effect of Caries Infiltration Technique and Fluoride Therapy on Microhardness of Enamel Carious Lesions[END_REF] of hypomineralized enamel. The mineral network initially weakened is incorporated into a resin. It forms a semi--natural biocomposite with good mechanical properties, which is what makes it such an attractive biomimetic material. Regarding the new boom in therapeutic resin infiltration, an increasing number of infiltrants are expected on the market in the coming years, making it necessary to assess and compare them. Some of them have been studied separately from the lesion or by using a porous medium mimicking the lesion [START_REF] Paris | Penetration coefficients of commercially available and experimental composites intended to infiltrate enamel carious lesions[END_REF], that is to say the characteristics obtained (contact angle, surface tension, viscosity, hardening) do not take into account the medium invested. To bridge this gap, our aim is to define a reliable and reproducible model mimicking most of the characteristics of hypomineralized enamel (summarized in Table 1). This makes it necessary to perform infiltration and mechanical tests on pairs of resin/biomimetic porous medium.

The use of natural hypomineralized enamel seems ideal, but as hypomineralization is not an indicator for extraction, such samples are very difficult to gather in sufficient numbers for systematic testing. Therefore artificial hypomineralized enamel [22--24] or models made of synthetic hydroxyapatite can be considered. However, these candidates must be dismissed because either they cannot provide a specimen with a calibrated volume (necessary for infiltration and mechanical tests) or the crystal form is not sufficiently close to that of natural crystal. As surprising as it may seem, deproteineized dentin could be a very interesting alternative in response to our specifications. First, dentin is formed by the same odontogenesis mechanisms as enamel. Enamel and dentin have the same hydroxyapatite chemistry. Secondly, the mineral density of dentin (50% by volume) is certainly lower than in sound enamel (87% by volume), but is still very close to that of hypomineralized enamel (60% to 70% by volume). Thirdly, the mechanical properties of dentin [25--26] (hardness from 0.81 to 1.19 GPa and a Young's modulus from 18 to 25GPa) are close to those of hypomineralized enamel. Fourthly, the use of dentin samples offers the advantage of allowing the preparation of calibrated samples compatible with laboratory tests. However dentin presents an abundant collagen matrix (mainly composed of type I collagen fibers) strongly linked to the crystal lattice. To obtain a porous mineral medium from the dentinal substrate, we propose to deproteinize the dentin to remove the collagen fibers and only keep the mineral matrix. Given the complexity of the organic matrix, deproteinization by heat treatment seems more relevant and efficient than selective chemical deproteinization.

The objective of this study is to evaluate the morphological and mechanical consequences of dentin deproteinization by heat treatment to validate the use of deproteinized dentin as a substrate mimicking hypomineralized enamel.

Material and methods

Sample preparation

Twenty non--carious human molars, extracted according to the protocols approved by the review board of the Dental Faculty of Paris--Descartes University, were used in this study.

They were stored directly in 1% chloramine--T solution. The teeth were prepared using a flat wet grinding machine (Planopol--3, STRUERS) to retain only the coronal dentin.

Enamel, cementum and radicular dentin were removed. Three teeth were kept for the control group (D0). Seventeen specimens were cut vertically into two equal parts to form two equivalent groups (D600 and D800). All the specimens were also acid etched with 37% phosphoric acid for 15s and then rinsed to remove the smear layer formed during the preparation procedures.

A conventional gradient ethanol dehydration process was applied to the D0 specimens reserved for SEM observation, and for the specimens of groups D600 and D800. In groups D600 and D800, the samples were deproteinized by heat treatment (see next section).

After the preparation procedure, all the specimens were stored in a desiccation room.

The specimens reserved for nanoindentation (Figure 1) were embedded before micron polishing. Specimens were rinsed in an ultrasound water bath (Transsonic 275,PROLABO) between each polishing step. Sound and hypomineralized enamel samples were also prepared in order to establish a comparison with the groups made from dentin. After embedding, the enamel samples and D0 group samples were stored in distilled water.

Samples from groups D600 and D800 were again subjected to ethanol dehydration.

Sample preparation and the methodology are summarized in Figure 1.

Deproteinization by Heat Treatment (HT)

In groups D600 and D800, the samples were deproteinized according to the thermodynamic principle of phase transition. The deproteinization process was performed under air using the VITA INCERAMAT 3 oven. The samples were heated to 600°C (D600) and 800°C (D800), temperatures at which organic components are known to be eliminated while preserving mineral components. That is to say, the temperatures chosen were higher than 420°C, the temperature at the organic matrix is degraded [START_REF] Bozec | Thermal Denaturation Studies of Collagen by Microthermal Analysis and Atomic Force Microscopy[END_REF] and lower than 850°C [START_REF] Bernache--Assollant | Sintering of calcium phosphate hydroxyapatite Ca 10 (PO 4 ) 6 (OH) 2 : I. Calcination and particle growth[END_REF], the temperature at which densification occurs due to the sintering behavior of hydroxyapatite grain.

Thermal Analysis

Thermogravimetry and differential thermogravimetry analysis (TGA and DTGA) were carried out specifically on D0 group samples in an STA 449 F3 Jupiter (Netzsch). The sample weight of sound dentin (D0 group) used for thermogravimetry analysis was 17 mg.

A heating rate of 5°C/min was employed for the run and the temperature range was 30--1000°C in a nitrogen and oxygen atmosphere of 80 ml/min and 60 ml/min, respectively.

Field emission scanning electron microscopy

Field emission scanning electron microscopy (FESEM) was used to compare the impact of the Heat Temperature (HT) on dentin qualitatively, especially in the crystal phase and for the porous microstructure, using an FEG LEO 1530 microscope (LEO Elektronen-mikroskopie GmbH) operated at low voltage (1-5 kV).

In each group, a specimen was randomly harvested and then fractured. The fractured samples were observed after being set on brass washers using a conductive coating.

Mercury Intrusion Porosimetry (MIP)

We used mercury porosimetry to quantify open porosity and assess the material pore size distribution. Mercury is a non--wetting liquid that will not naturally penetrate into the pore spaces of the sample. An incremental pressure was applied to the mercury so it entered the pores of the sample progressively. At each pressure step, the volume of mercury entering the sample was measured. The applied pressure P was linked to the pore diameter D via the surface tension of mercury γ and its contact angle θ using the Jurin--Laplace law: A mercury porosimeter (Micromeritics Autopore IV, Micromeritics) was used with a small sample holder (penetrometer 13--0609). The sample holder containing the dentin slices was weighed using an electronic balance with an accuracy of 0.1 mg (Sartorius TE214S, Labandco), then assembled on the porosimeter. The gas within the penetrometer was removed completely before the latter was filled with mercury under low pressure (approximately from 0.003 to 0.20 MPa, corresponding to the pore size between 420 and 6μm). After removing the sample holder from the low pressure cell, the assembly (penetrometer, sample and mercury) was weighed. The different weight data were collected in order to use the penetrometer as a pycnometer. Then, the penetrometer (containing the sample immersed in mercury) was inserted in the high pressure cell. Pressures from 0.20 to 200 MPa (corresponding to pore sizes between 6 μm to 6 nm) were applied. The volume of mercury penetrating into the sample was recorded at each pressure step. The contact angle and surface tension of mercury were set beforehand for all the tests at 130° and 485 dyn/cm [START_REF] Mikijelj | Equivalence of surface areas determined by nitrogen adsorption and by mercury porosimetry[END_REF], respectively. To obtain reliable results, the manufacturer recommends a final mercury intrusion volume between 20 and 80% of the stem volume. To fulfill this condition, four samples, i.e. four half teeth, were used for each test. Four tests per group (D600 and D800) were performed: D600T1, D600T2, D600T3, D600T4 and D800T1, D800T2, D800T3, D800T4.

As shown in Figure 1, groups D600 and D800 were obtained from the same teeth. The samples were fractured prior to testing in order to facilitate pore access.

Nanoindentation

The indentation experiments were performed using a nanoindenter (XP, MTS). The specimens were mounted on a support to ensure the exposure of a flat surface at right angles to the indenter. They were indented with a calibrated Berkovich indenter [START_REF] Huang | Nano--indentation characterisation of natural carious white spot lesions[END_REF].

Samples of groups D0, D600 and D800 were mapped systematically with spacings of 100 μm between individual indentations, in order to scan a surface of 1mm 2 , i.e. one hundred acquisitions. The indenter tip was set to apply a load of 25mN held for 5s. The force-displacement curve of loading and unloading was used to calculate the mechanical properties (elastic modulus and hardness) of each indentation point.

The same procedure was also reproduced on sound and hypomineralized enamel samples.

Statistics

Porosity and mechanical property data were analyzed using the Kruskal--Wallis test, which is useful for the 2 by 2 comparison of batches with different variances. The significance level was fixed at p=0.05.

Results

General observation

After HT, the dentin slices presented a color change. The specimens of groups D600 and D800 became "dark gray / bluish" and "light gray", respectively, compared to D0 which was light yellow (Figure 2).

Thermogravimetric analysis (TGA)

Figure 3 shows the TGA curve (sample weight as a function of temperature) obtained for the sound dentin (group D0) heated in a controlled atmosphere. It shows continuous weight loss throughout the HT. The weight loss increased substantially up to 530°C, after which it slowed down considerably. The D0 specimen fell from 100% to 73.76%, and from 73.76% to almost 72% at temperatures from 40°C to 530°C and 530°C to 980°C, respectively; corresponding to a weight loss of 26.24% in the first period of HT and 1.76% for the second period. Weight loss after 530°C was insignificant.

Four peaks of weight loss can be identified on the DTGA curve (Figure 3) at different temperatures. The first peak weight loss can be seen at 130°C. The weight of the specimen falls to 97.1%. Another substantial weight loss peak occurs at 330°C, with a reduction sample weight. The third peak at 450°C overlaps the end of the second peak.

Hence, the third peak appears less marked and can be considered as part of the second peak. A weak final peak of weight loss was recorded at 710°C.

The maximum temperature at the end of each weight loss peak (DTGA curve, illustrated in Figure 3) was measured to establish the contribution of each reaction to weight loss.

Scanning Electron Microscopy (SEM) observations

The SEM observation allowed qualitative analysis of the interconnected porous network formed by heat treatment. Representative FESEM pictures of the control human dentin samples (group D0) and those heated to 600°C (group D600) and 800°C (group D800) are shown in Figure 4.

In the D0 group, the tubular collar surface was smooth and seemed to be impermeable except for a few scattered secondary tubules. The intertubular dentin was inhomogeneous but void--free. Group D600 exhibited greater roughness after the calcination of the dentin than group D800, revealing porosity from the peritubular dentin up to the intertubular dentin. The acicular crystal morphology and size was more or less preserved. On the other hand, for group D800, crystal size and morphology exhibited significant changes. The crystalline diameter was about 100 nm and the crystals acquired a rounded, almost spherical structure. Many voids can be observed between the spherical crystals. The sizes of the crystallites are roughly 10nm and 100nm for group D600 and D800, respectively (Figure 4).

Mercury Intrusion Porosimetry (MIP)

We used MIP to quantitatively evaluate the effect of HT on the dentin porous network.

Figure 5 presents the pore size distribution in terms of cumulative and incremental curves and clearly illustrates the effect of HT on the porosity of human dentine. Figure 6 shows the distribution of the average porosity related to pore size for each group (D0, D600 and D800).

Group D0 presents a single pore size around 0.60 μm whereas group D600 and D800 present two distinct pore sizes around 0.9μm and 20nm and around 0.9μm and 90nm, respectively. The distribution of the second pore size is different between these two groups and lies respectively between 14 nm and 32 nm and between 60 nm and 100 nm (Figure 6). The total porosity of group D600 was recorded at 39% (±1.2%), including 36% (±1.4%) arising from the second porosity. It is slightly lower for D800 with a total porosity of 37% (±3.4%), including 33% (±3.5%) from the second porosity (Figure 7 and Table 2).

Modulus of elasticity and hardness

Hypomineralization considerably reduces the mechanical properties of enamel. The sound enamel had a hardness and an elastic modulus of 1.9GPa (±0.6GPa) and 85.5GPa (±15.7GPa), respectively, which fell to 0.56GPa (±0.40GPa) and 27.3GPa (±5.7GPa) when hypomineralization occurred. HT seemed to reduce the hardness of dentin slightly. Group D0 presented a hardness of 0.52GPa (±0.13GPa) versus 0.32GPa (±0.13GPa) and 0.36GPa (±0.10GPa) for groups D600 and D800. In contrast, HT seemed to increase the local elastic modulus of dentin slightly. Group D0 presented an elastic modulus value of 19.32GPa (±2.8 GPa) versus 27.32GPa (±5.7GPa) and 24.95GPa (±4.0GPa) for D600 and D800 (Figure 8). Total porosity did not differ significantly between groups D600 and D800 (p>0.05) but the size of the nanopores revealed by heat treatment differed significantly between those groups (p<0.05).

From the mechanical standpoint, groups D600, D800 and hypomineralized enamel did not differ significantly in terms of local elastic modulus and hardness (p>0.05). These three groups all differed from sound enamel in terms of local elastic modulus and hardness (p<0.05).

Discussion

This study aimed at validating the use of dentin deproteinized by heat treatment as a porous medium mimicking hypomineralized enamel. To fulfill this objective, thermogravimetric analysis (TGA), porosimetry tests, FESEM observations and nanoindentation tests were performed. The objective of TGA was to choose and validate the heat treatment temperature whereas the porosimetry tests and SEM observations were carried out to investigate, quantitatively and qualitatively, the porous structure revealed by deproteinization. Finally, the nanoindentation tests were performed to evaluate whether the mechanical properties of the newly formed porous medium are close enough to those of hypomineralized enamel.

First, TGA and optical microscope observations are discussed. Our results showed a color change during thermal treatment. The belated volatilization of organic residues of collagen in a calcified matrix (such as dentin) is the hypothesis advanced by numerous authors [31--33] to explain color change as a function of heating temperature. Consistent with the observations of Fan et al. [START_REF] Fan | Consequence of heat--treatement on properties of dentin (abstract)[END_REF] (dentin is amber at 200°C, black at 400°C, dark grey at 600°C, light grey at 800°C and white at 950°C), dark grey specimens were obtained after heating at 600°C, and light grey specimens at 800°C. This color change according to temperature was pointed out by Ooi et al. [START_REF] Ooi | Properties of hydroxyapatite produced by annealing of bovine bone[END_REF] in a study on bone. The persistence of a darker colors observed on dentin after heating seems to indicate incomplete removal of certain organic chemical components. These may be retained in calcined dentin in the form of oxidized residues or carbonates. Together with optical microscopy observations and in accordance with the literature [34--36], the four peaks of weight loss identified on the DTGA curve (Figure 3) can be linked to:

• proteic denaturation due to water evaporation that breaks the hydrogen bonds and thus increases the sensitivity of the freed collagen to thermal denaturation [START_REF] Armstrong | Denaturation Temperatures of Dentin Matrices. I. Effect of Demineralization and Dehydration[END_REF] (130°C),

• decomposition of the organic substrate [START_REF] Sakae | Thermal Stability of Mineralized and Demineralized Dentin: A Differential Scanning Calorimetric Study[END_REF] (330°C),

• residual decomposition of the organic substrate (450°C),

• the loss of carbonates [START_REF] Nordback | Effect of heat on the carbonate content of dentin[END_REF] (710°C).

Thus, the TGA results confirmed that dentin deproteinization occurs at high temperature (higher than 500°C). The challenge to obtain specimens totally deproteinized by heat treatment at 600°C and 800°C was successful for groups D600 and D800. This was confirmed by the FESEM observations and porosimetry tests discussed below.

The porosimetry tests presented two main pore sizes around 0.9μm and 20nm and around 0.9μm and 90nm for groups D600 and D800, respectively, whereas control group D0 only presented one pore size around 0.6μm. The first pore size detected corresponded to the tubules and microbranchings that were present in the three groups. The shift of pore size towards a larger pore size with heat treatment indicates that the tubules were affected by the heat treatment. But the main finding was that heat treatment allows the formation of new porosity following the removal of the collagen fibers (confirmed by FESEM observations). In group D600, the new pore size was around 50nm whereas in group D800, it was around 100nm. This difference between the two groups can be explained by the transformation of crystallites occurring at 800°C, as could be seen in the FESEM observations. The HT did not alter dentin microstructure (tubular, peritubular and intertubular compartments), but at 600°C, the crystals remained acicular and small while at 800°C, the grains grew and become spherical, a phenomenon already reported by Locardi et al. [START_REF] Locardi | Thermal behaviour of hydroxyapatite intended for medical applications[END_REF]. As the temperature increased, a series of reactions in the non--stoichiometric HAP of the dentin occurred, marked in the case of our study by the decomposition of HAP into βtricalcium phosphate (β--TCP) around 700°C. For Bernache--Assollant et al. [START_REF] Bernache--Assollant | Sintering of calcium phosphate hydroxyapatite Ca 10 (PO 4 ) 6 (OH) 2 : I. Calcination and particle growth[END_REF], hydroxyapatite crystal growth would occur even from 600°C. The increase in crystallite size occurring during the conversion in β--TCP was responsible for a decrease in specific surface area. That explains, firstly, the increase in pore size for D800 (passing from several tens of nanometers to a hundred nanometers) and, secondly, the slight decrease of total porosity at this calcination temperature. However, it is noteworthy that the porous mineral network obtained remained interconnected and open despite calcination at high temperature.

Finally, the crystalline transformation of HAP into TCP at a temperature higher than 700°C excluded the use of D800 specimens as a resin infiltration support model. The total porosity of group D600 was recorded at 39%, including 36% arising from Protein Free Porosity (PFP). It was slightly lower for D800 with a total porosity of 37%, including 33% from PFP (Figure 7). The total porosity of sound dentin (group D0) was much lower (4%) because it was dependent only on tubular porosity (DTP). The porosity due to the removal of collagen fibers in groups D600 and D800 (33% and 36%, respectively) met our specifications in terms of porosity (set at between 20 and 40%, as presented in Table 3). It can be seen that the natural porosity of dentin remains but its share is negligible compared to the porosity newly created by heat treatment.

In addition to validating the transformation of dentin into a satisfying mineral porous medium model by heat treatment, the experiment carried out provided interesting information on its composition and micro--and nano--structure. According to the total porosity of each group, the volume percentage of the mineral phase appears slightly underestimated in the literature [40--41], but a further study at lower temperature should be conducted to confirm this finding. Indeed, the study of dentin thermal deproteinization allowed assessing the average total porosity of deproteinized dentin (39.4%), and, by extension, its remaining mineral volume of 60.6%, whereas Driessens and Verbeeck [42] and Mjör and Fejerskov [START_REF] Mjor | Histology of the Human Tooth[END_REF] indicated a mineral volume of 50% for this same substrate. However, Vennat et al. [START_REF] Vennat | Demineralized dentin 3D porosity and pore size distribution using mercury porosimetry[END_REF] showed a porosity of roughly 4% and 70% for intact and demineralized dentin, respectively, that is to say that the tubules occupy 4% of dentin volume while collagen fibers occupy 30%, thus we can deduce that the mineral part is roughly 66%, which is in good agreement with our findings. It is noteworthy that the results for group D800 were not taken into account here, as the sintering phenomenon at this temperature would have led to overevaluating the mineral volume.

The removal of the organic phase precluded the structural organization and the location of the apatite crystals. The average pore size obtained with D600 was lower than the average diameter of a collagen fiber (83--100nm for hydrated fibers and 75--105nm for dehydrated fibers, [START_REF] Habelitz | In situ atomic force microscopy of partially demineralized dentin collagen fibrils[END_REF]). This can be explained by the fact that crystals were not only located around the collagen fibers. The crystal is both interfibrillar and intrafibrillar, as explained by Pashley et al. [START_REF] Pashley | State of the art etch--and--rinse adhesives[END_REF]. It should also be noted that MIP detects pore entry sizes thus it can lead to an overestimation of small pore sizes.

Dentin keeps its structural integrity after complete deproteinization by heating. Therefore observation of the heated samples showed that despite the removal of their proteic phase, their volume and tubular architecture was fully preserved. As shown by Chen et al. [START_REF] Chen | Minerals Form a Continuum Phase in Mature Cancellous Bone[END_REF] for bones, the mineral phase of dentin seems to be arranged in a continuum. The mineral phase of dentin therefore does not seem be a dispersed phase in an organic matrix.

From the mechanical point of view, the nanoindentation tests performed on the heat-treated groups were also in good agreement with the specifications. Table 3 highlights the overall good agreement between the specifications and the porous medium formed. Group D600 (heat treatment up to 600°C) seemed to be the most satisfying.

The mineral porous medium presented here could also be considered for its use as a scaffold in tissue engineering applications. In tissue engineering, a porous scaffold is required to receive cells and enable their growth. The optimum scaffold architecture is not known but the following design variables are key determinants:

• the provision of adequate space for growth and the development of sufficient transport pathways within the porous material,

• the ability to be broken down by osteoclasts, • surface mechanical properties propitious for future cells (for example, soft or hard tissue).

Dentin has already been used as a bone substitute [46--47] and as a scaffold for dental tissue engineering [START_REF] Petite | Tissue--engineered bone regeneration[END_REF], but deproteinized dentin has not yet been envisaged for this purpose to our knowledge. The porous medium made of deproteinized dentin characterized here is interesting because of its porosity (39% for group D600), but above all because it is composed of natural hydroxyapatite [START_REF] Figueiredo | Effect of the calcination temperature on the composition and microstructure of hydroxyapatite derived from human and animal bone[END_REF] (instead of synthetic hydroxyapatite which is most often proposed and studied [START_REF] Petite | Tissue--engineered bone regeneration[END_REF]). Thus it exhibits a roughness close to the aragonite shaped crystals found in coral (proven to be a very efficient scaffold for bone reconstruction [48--49]) and it is known to have an enhanced biodegradation rate compared with synthetic bioceramics. Also, its mechanical properties can make it a good candidate to stimulate cells in order to create mineralized tissues.

Conclusion

The challenge to reproduce a biomimetic porous medium to study the effect of infiltrants on hypomineralized enamel raises numerous issues. The surprising idea of deproteinizing dentin by heat treatment is attractive and it proved to be suitable in terms of porosity, pore size range and mechanical properties. The calcination temperature of 600°C was the most satisfying, leading to a total porosity of 39%, with two pore sizes, one due to the tubules (around 0.9 µm) and one due to the elimination of the collagen fibrils (around 20nm). The porosity due to the elimination of the collagen fibrils was 36%. This biomimetic porous medium should be used for evaluating the infiltration kinetics and mechanical properties of new treatments using hypomineralized dentin.

The mineral porous medium presented here can also be considered as a scaffold for tissue engineering since it is composed of natural hydroxyapatite and has interesting morphological characteristics.

Acknowledgements

The authors would like to thank Mrs. F. Garnier, Mr. T. Reiss and Mr. N. Roubier for their technical assistance in performing the SEM observations, MIP experiments and mechanical characterization, respectively.


Erven Rohou

Bharath Narasimha

Swamy André

Branch Prediction and the Performance of Interpreters -Don't Trust Folklore

Interpreters have been used in many contexts. They provide portability and ease of development at the expense of performance. The literature of the past decade covers analysis of why interpreters are slow, and many software techniques to improve them. A large proportion of these works focuses on the dispatch loop, and in particular on the implementation of the switch statement: typically an indirect branch instruction. Folklore attributes a significant penalty to this branch, due to its high misprediction rate. We revisit this assumption, considering state-of-the-art branch predictors and the three most recent Intel processor generations on current interpreters. Using both hardware counters on Haswell, the latest Intel processor generation, and simulation of the IT-TAGE, we show that the accuracy of indirect branch prediction is no longer critical for interpreters. We further compare the characteristics of these interpreters and analyze why the indirect branch is less important than before.

Introduction

Interpreters go back to the infancy of computer science. At some point, just-in-time (JIT) compilation technology matured enough to deliver better performance, and was made popular by Java [START_REF] Cramer | Compiling Java just in time[END_REF]. Writing a JIT compiler, though, is a complicated task. Conversely, interpreters provide ease of implementation, and portability, at the expense of speed.

Interpreters are still widely used. They are much easier to develop, maintain, and port applications on new architectures. Some languages used by domain scientists are executed mainly through interpreters, e.g. R, Python, Matlab... Some properties of widely adopted languages, such as dynamic typing, also make it more difficult to develop efficient JIT compilers. These dynamic features turn out to be heavily used in real applications [START_REF] Richards | An analysis of the dynamic behavior of JavaScript programs[END_REF]. On lower-end systems, where short time-to-market is key, JIT compilers may also not be commercially viable, and they rely on interpreters.

Scientists from both CERN and Fermilab report [START_REF] Naumann | The role of interpreters in high performance computing[END_REF] that "many of LHC experiments' algorithms are both designed and used in interpreters". As another example, the need for an interpreter is also one of the three reasons motivating the choice of Jython for the data analysis software of the Herschel Space Observatory [START_REF] Wieprecht | The HERSCHEL/PACS Common Software System as Data Reduction System[END_REF]. Scientists at CERN also developed an interpreter for C/C++ [START_REF] Davidson | Cint: a RISC interpreter for the C programming language[END_REF].

Although they are designed for portability, interpreters are often large and complex codes. Part of this is due to the need for performance. The core of an interpreter is an infinite loop that reads the next bytecode, decodes it, and performs the appropriate action. Naive decoding implemented in C consists in a large switch statement (see Figure 1 (a)), that gets translated into a jump table and an indirect jump. Conventional wisdom states that this indirect jump incurs a major performance degradation on deeply pipelined architectures because it is hardly predictable (see Section 6 for related work).

The contributions of this paper are the following.

• We revisit the performance of switch-based interpreters, focusing on the impact the indirect branch instruction, on the most recent Intel processor generations(Nehalem, Sandy Bridge and Haswell) and current interpreted languages (Python, Javascript, CLI). Our experiments and measures show that on the latest processor generation, the performance of the predictors and the characteristics of interpreters make the indirect branch much less critical than before. The global branch misprediction rate observed when executing interpreters drops from a dramatic 12-20 MPKI range on Nehalem to a only 0.5-2 MPKI range on Haswell.

• We evaluate the performance of a state-of-the-art indirect branch predictor, ITTAGE [START_REF] Seznec | A case for (partially) TAgged GEometric history length branch prediction[END_REF], proposed in the literature on the same interpreters, and we show that, when executing interpreters, the branch prediction accuracy observed on Haswell and on ITTAGE are in the same range.

The rest of this paper is organized as follows. Section 2 motivates our work: it analyzes in more details the performance of switch-based interpreters, it introduces jump threading, and measures its impact using current interpreters. Section 3 reviews the evolution of branch prediction over the last decades, and presents the state-of-the-art branch predictors TAGE for conditional branches and ITTAGE [START_REF] Seznec | A case for (partially) TAgged GEometric history length branch prediction[END_REF] for indirect branches. Section 4 presents experimental setup. In Section 5, we present our experimental results and our findings on branch prediction impact on interpreter performance. Section 6 reviews related work. Section 7 concludes. Terminology This work is concerned with interpreters executing bytecodes. This bytecode can be generated statically before actual execution (as in the Java model), just before execution (as in Python, which loads a pyc file when already present, and generates it when missing), or at runtime (as in Javascript). What is important is that the execution system is not concerned with parsing, the code is already available in a simple intermediate representation. Throughout this paper, the word bytecode refers to a virtual instruction (including operands), while opcode is the numerical value of the bytecode (usually encoded as a C enum type). Instructions refer to native instructions of the processor. The virtual program counter is the pointer to the current executed bytecode. Dispatch is the set of instructions needed to fetch the next bytecode, decode it (figure out that it is e.g. an add) and jump to the chunk of code that implements its semantics, i.e. the payload (the code that adds of the appropriate values).

Performance of Interpreters

Quoting Cramer et al. [START_REF] Cramer | Compiling Java just in time[END_REF], Interpreting bytecodes is slow.

An interpreter is basically an infinite loop that fetches, decodes, and executes bytecodes, one after the other. Figure 1 (a) illustrates a very simplified interpreter written in C, still retaining some key characteristics. Lines 1 and 13 implement the infinite loop. Line 2 fetches the next bytecode from the address stored in virtual program counter vpc, and increments vpc (in case the bytecode is a jump, vpc must be handled separately). Decoding is typically implemented as a large switch statement, starting at line 3. Many interpreted languages implement an evaluation stack. This is the case of Java, CLI, Python (Dalvik is a notable exception [START_REF] Ehringer | The Dalvik virtual machine architecture[END_REF]). Lines 5-7 illustrate an add

The principal overhead of interpreters comes from the execution of the dispatch loop. Every bytecode typically requires ten instructions when compiled directly from standard C (as measured on our own interpreter compiled for x86, described in Section 5.2.3). See Figure 1 (b) for a possible translation of the main loop to x86. This compares to the single native instruction needed for most bytecodes when the bytecode is JIT compiled. Additional costs come from the implementation of the switch statement. All compilers we tried (GCC, icc, LLVM) generate a jump table and an indirect jump instruction (line 7 of Figure 1 (b)). This jump has hundreds of potential targets, and has been previously reported to be difficult to predict [START_REF] Ertl | The behavior of efficient virtual machine interpreters on modern architectures[END_REF][START_REF] Ertl | Optimizing indirect branch prediction accuracy in virtual machine interpreters[END_REF][START_REF] Mccandless | Compiler techniques to improve dynamic branch prediction for indirect jump and call instructions[END_REF], resulting typically in an additional 20 cycle penalty. Finally, operands must be retrieved from the evaluation stack, and results stored back to it (lines 9-10) and the stack adjusted (line 11), while native code would have operands in registers in most cases (when not spilled by the register allocator).

A minor overhead consists in two instructions that compare the opcode value read from memory with the range of valid bytecodes before accessing the jump table (lines [START_REF] Chang | Target prediction for indirect jumps[END_REF][START_REF] Costa | CLI back-end in GCC[END_REF]. By construction of a valid interpreter, values must be within the valid range, but a compiler does not have enough information to prove this. However, any simple branch prediction will correctly predict this branch.

This paper addresses the part of the overhead due to the indirect branches. We revisit previous work on the predictability of the branch instructions in interpreters, and the techniques proposed to address this cost. Other optimizations related to optimizing the dispatch loop are briefly reviewed in Section 6.

Jump threading

As mentioned, a significant part of the overhead of the dispatch loop is thought to come from the poorly predicted indirect jump that implements the switch statement. Jump threading is the name of an optimization that addresses this cost. It basically bypasses the mechanism of the switch, and jumps from one case entry to the next. Figure 2 illustrates how this can be written. Jump threading, though, cannot be implemented in standard C. It is commonly implemented with the GNU extension named Labels as Values 1 . And while many compilers now support this extensions (in particular, we checked GCC, icc, LLVM), older versions and proprietary, processor specific compilers may not support it.

The intuition behind the optimization derives from increased branch correlation: firstly, a single indirect jump with many targets is now replaced by many jumps; secondly, each jump is more likely to capture a repeating sequence, simply because application bytecode has patterns (e.g. a compare is often followed by a jump).

Many interpreters check if this extension is available in the compiler to decide whether to exploit it, or to revert to the classical switch-based implementation. Examples include Javascript and Python, discussed in this paper. Previous work [START_REF] Ertl | Optimizing indirect branch prediction accuracy in virtual machine interpreters[END_REF] reports that it is also the case for Ocaml, YAP and Prolog. This double implementation, however, results in cumbersome code, #ifdefs, as well as the need to disable several code transformations that could de-optimize it (the source code of Python mentions global common subexpression elimination and cross-jumping).

Motivation Example

Current versions of Python-3 and Javascript automatically take advantage of threaded code when supported by the compiler. The implementation consists in two versions (plain switch and threaded code), one of them being selected at compile time, based on compiler support for the Labels as Values extension. Threaded code can be easily disabled though the configure script or a #define.

In 2001, Ertl and Gregg [START_REF] Ertl | The behavior of efficient virtual machine interpreters on modern architectures[END_REF] observed that:

"for current branch predictors, threaded code interpreters cause fewer mispredictions, and are almost twice as fast as switch based interpreters on modern superscalar architectures".

The current source code of Python-3 also says:

"At the time of this writing, the threaded code version is up to 15-20% faster than the normal switch version, depending on the compiler and the CPU architecture."

We tracked this comment back to January 2009. We experimented with Python-3.3.2, both with and without threaded code, and the Unladen Swallow benchmarks (selecting only the benchmarks compatible with Python 2 and Python 3, with flag -b 2n3). Figure 3 (a) shows the performance improvement due to threaded code on three microarchitectures: Nehalem, Sandy Bridge, and Haswell.

Nehalem shows a few outstanding speedups (in the 30 %-40 % range), as well as Sandy Bridge to a lesser extent, but the average speedups (geomean of individual speedups) for Nehalem, Sandy Bridge, and Haswell are respectively 10.1 %, 4.2 %, and 2.8 % with a few outstanding values for each microarchitecture. The benefits of threaded code decreases with each new generation of microarchitecture.

Python-2 also supports a limited version of threading, implemented in standard C, aimed at the most frequent pairs of successive opcodes. Nine pairs of opcodes are identified and hard-coded in the dispatch loop. The speedups are reported in Figure 3 (b). Respective averages are 2.8 %, 3.2 % and 1.8 % for Nehalem, Sandy Bridge, and Haswell.

As illustrated by this simple experiment, the speedup brought by jump threading on modern hardware is much less it used to be. And a better branch prediction is not the single factor contributing to the speedup. As already observed by Piumarta and Riccardi [START_REF] Piumarta | Optimizing direct threaded code by selective inlining[END_REF] threaded code also executes fewer instructions because part of the dispatch loop is bypassed. For example, on Python-3, we measured the average reduction in number of instructions to be on average 3.3 %, in the same range as the performance gain on the three tested architectures.

Revisiting Conventional Wisdom

Conventional wisdom considers that the indirect branch that drives the dispatch loop of a switch-based interpreter is inherently difficult to predict. Much effort has been devoted -in the literature and in actual source code -to improving its predictability and reducing its overhead. This was certainly true in the past, and we review related work in Section 6. However, branch predictors have significantly evolved, and they achieve much better performance. In this paper, we study the properties of current interpreters and state-of-theart branch prediction, and we show that the behavior of the main indirect branch is now a minor issue.

(Indirect) Branch Predictors

State-of-the-art

Many proposals have been introduced for improving the accuracy of conditional branch prediction during the two past decades, e.g. two-level branch prediction [START_REF] Yeh | Two-level adaptive training branch prediction[END_REF], hybrid predictors [START_REF] Mcfarling | Combining branch predictors[END_REF], de-aliased predictors [START_REF] Lee | The bi-mode branch predictor[END_REF][START_REF] Michaud | Trading conflict and capacity aliasing in conditional branch predictors[END_REF][START_REF] Sprangle | The agree predictor: A mechanism for reducing negative branch history interference[END_REF], multiple history length use [START_REF] Seznec | Design tradeoffs for the Alpha EV8 conditional branch predictor[END_REF], and more recently perceptroninspired predictors [START_REF] Jiménez | Dynamic branch prediction with perceptrons[END_REF] and geometric history length predictors [START_REF] Seznec | Analysis of the O-GEometric History Length Branch Predictor[END_REF][START_REF] Seznec | A case for (partially) TAgged GEometric history length branch prediction[END_REF]. All these propositions have influenced the design of the predictors embedded in state-of-art processors.

While effective hardware predictors probably combine several prediction schemes (a global history component, a loop predictor and maybe a local history predictor), TAGE [START_REF] Seznec | A new case for the TAGE branch predictor[END_REF][START_REF] Seznec | A case for (partially) TAgged GEometric history length branch prediction[END_REF] is generally considered as the state-of-the-art in global history based conditional branch prediction. TAGE features several (partially) tagged predictor tables. The tables are indexed with increasing global history length, the set of history lengths forming a geometric series. The prediction is given by the longest hitting table. TAGE predictors featuring maximum history length of several hundreds of bits can be implemented in real hardware at an affordable storage cost. Therefore TAGE is able to capture correlation between branches distant by several hundreds or even thousands of instructions.

For a long time, indirect branch targets were naively predicted by the branch target buffer, i.e. the target of the last occurrence of the branch was predicted. However the accuracy of conditional branch predictors is becoming higher and higher. The penalties for a misprediction on a conditional branch or on an indirect branch are in the same range. Therefore even on an application featuring a moderate amount of indirect branches, the misprediction penalty contribution of indirect branches may be very significant if one neglects the indirect branch prediction. Particularly on applications featuring switches with many case statements, e.g. interpreters, the accuracy of this naive prediction is quite low. To limit indirect branch mispredictions, Chang et al. [START_REF] Chang | Target prediction for indirect jumps[END_REF] propose to leverage the global (conditional) branch history to predict the indirect branch targets, i.e. a gshare-like indexed table is used to store the indirect branch targets. However Driesen and Holzle [START_REF] Driesen | Accurate indirect branch prediction[END_REF] point out that many indirect branches are correctly predicted by a simple PC-based table, since at execution time they feature a single dynamic target. They proposed the cascaded indirect branch predictor to associate a PC-based table (might be the branch target buffer) with a tagged (PC+global branch history) indexed table.

More recently, Seznec and Michaud [START_REF] Seznec | A case for (partially) TAgged GEometric history length branch prediction[END_REF] derived IT-TAGE from their TAGE predictor. Instead of simple conditional branch directions, ITTAGE stores the complete target in tagged tables indexed with increasing history lengths which form a geometric series. As for TAGE, the hitting table featuring the longest history length provides the prediction. At the recent 3rd championship on branch prediction in 20112 , TAGE-based (resp. ITTAGE-based) predictors were shown to outperform other conditional branch predictors (resp. indirect predictors).

Intuition of ITTAGE on interpreters

TAGE performs very well at predicting the behavior of conditional branches that exhibit repetitive patterns and very long patterns. Typically when a given (maybe very long) sequence of length L branches before the current program counter was always biased in a direction in the past, then TAGE -provided it features sufficient number of entrieswill correctly predict the branch, independently of the minimum history le needed to discriminate between the effective biased path and another path. This minimum path is captured by one of the tables indexed with history longer than le. With TAGE, the outcomes of branches correlated with close branches are captured by short history length tables, and the outcomes of branches correlated with very distant branches are captured by long history length tables. This optimizes the application footprint on the predictor. The same applies for indirect branches.

When considering interpreters, the executed path is essentially the main loop around the execution of each bytecode. When running on the succession of basic block bytecodes, the execution pattern seen by the switch reflects the control path in the interpreted application: in practice the history of the recent targets of the jump is the history of opcodes. For instance, if this history is -load load add load mul store add-and if this sequence is unique, then the next opcode is also uniquely determined. This history is in some sense a signature of the virtual program counter, it determines the next virtual program counter.

When running interpreters, ITTAGE is able to capture such patterns and even very long patterns spanning over several bytecode basic blocks, i.e. to "predict" the virtual program counter. Branches bytecodes present the particularity to feature several possible successors. However, if the interpreted application is control-flow predictable, the history also captures the control-flow history of the interpreted application. Therefore ITTAGE will even predict correctly the successor of the branch bytecodes.

Experimental Setup

This section details our interpreters and benchmarks. We discuss how we collect data for actual hardware and simulation, and we make sure that both approaches are consistent.

Interpreters and Benchmarks

We experimented with switch-based (no threading) interpreters for three different input languages: Javascript, Python, and the Common Language Infrastructure (CLI, aka .NET), and several inputs for each interpreter. Javascript benchmarks consist in Google's octane suite3 as of Feb 2014, and Mozilla's kraken4 . For Python, we used the Unladen Swallow Benchmarks. Finally, we used a subset of SPEC 2000 (train input set) for CLI. All benchmarks are run to completion (including hundreds of hours of CPU for the simulation). See Table 1 for an exhaustive list.

We used Python 3.3.2. The motivation example of Section 2 also uses Python 2.7.5. Unladen Swallow benchmarks were run with the flag --rigorous. We excluded iterative_count, spectral_norm and threaded_count from the suite because they were not properly handled by our measurement setup. Javascript experiments rely on SpiderMonkey 1.8.5

We used GCC4CLI [START_REF] Costa | CLI back-end in GCC[END_REF] to compile the SPEC 2000 benchmarks. It is a port of GCC that generates CLI from C. The CLI interpreter is a proprietary virtual machine that executes applications written in the CLI format. Most of standard C is supported by the compiler and interpreter, however a few features are missing, such as UNIX signals, setjmp, or some POSIX system calls. This explains why a few benchmarks are missing (namely: 176.gcc, 253.perlbmk, 254.gap, 255.vortex, 300.twolf). This is also the reason for not using SPEC 2006: more unsupported C features are used, and neither C++ nor Fortran are supported.

The interpreters are compiled with Intel icc version 13, using flag -xHost that targets the highest ISA and processor available on the compilation host machine. Some compilers force the alignment of each case entry to a cache line, presumably in an attempt to fit short entries to a single line, thus improving the performance. The downside is that many more targets of the indirect branch alias in the predictor because fewer bits can be used to disambiguate them. Visual inspection confirmed that this is not the case in our setup. McCandless and Gregg [START_REF] Mccandless | Compiler techniques to improve dynamic branch prediction for indirect jump and call instructions[END_REF] reported this phenomenon and developed a technique that modifies the alignment of individual case entries to improve the overall performance. We manually changed the alignment of the entries in various ways, and observed no difference in performance.

Branch Predictors

We experimented with both commercially available hardware and recent proposals in the literature. Section 4.2.3 discusses the coherence of actual and simulated results. Unfortunately, neither architecture has hardware counters for retired indirect jumps, but for "speculative and retired indirect branches" [START_REF]Intel. Intel 64 and IA-32 Architectures Software Developer's Manual[END_REF]. It turns out that non-retired indirect branches are rare. On the one hand, we know that the number of retired indirect branches is at least equal to the number of executed bytecodes. On the other hand, the value provided by the counter may overestimate the number of retired indirect branches in case of wrong path execution. That is: n bytecodes ≤ n retired ≤ n speculative or equivalently:

1 ≤ n retired n bytecodes ≤ n speculative n bytecodes
where n speculative is directly read from the PMU, and n bytecodes is easily obtained from the interpreter statistics.

In most cases (column ind/bc of Tables 3,4, 5), the upper bound is very close to 1, guaranteeing that non retired indirect branches are negligible. In the remaining cases, we counted the number of indirect branches with a pintool [START_REF] Luk | Pin: building customized program analysis tools with dynamic instrumentation[END_REF] and confirmed that the PMU counter is a good estimate of retired indirect branches.

TAGE -Simulation

We also experimented with a state-of-the-art branch predictor from the literature: TAGE and ITTAGE [START_REF] Seznec | A case for (partially) TAgged GEometric history length branch prediction[END_REF]. The performance is provided through simulation of traces produced by Pin [START_REF] Luk | Pin: building customized program analysis tools with dynamic instrumentation[END_REF]. We used two (TAGE+ITTAGE) configurations. Both have 8 KB TAGE. TAGE1 assumes a 12.62 KB IT-TAGE, TAGE2 assumes a 6.31 KB ITTAGE (see Table 2).

Coherence of Measurements

Our experiments involve different tools and methodologies, namely the PMU collected by the Tiptop tool [START_REF] Rohou | Tiptop: Hardware Performance Counters for the Masses[END_REF] on existing hardware, as well as results of simulations driven by traces obtained using Pin [START_REF] Luk | Pin: building customized program analysis tools with dynamic instrumentation[END_REF]. This section is about confirming that these tools lead to comparable instruction counts, therefore experiment numbers are comparable. Potential discrepancies include the following:

• non determinism inherent to the PMU [START_REF] Weaver | Can hardware performance counters produce expected, deterministic results?[END_REF] or the system/software stack [START_REF] Mytkowicz | Producing wrong data without doing anything obviously wrong! In ASPLOS[END_REF];

• the x86 instruction set provides a rep prefix. The PMU counts prefixed instructions as one (made of many microops), while pintools may count each separately;

• Pin can only capture events in user mode, while the PMU has the capability to monitor also kernel mode events;

• tiptop starts counting a bit earlier than Pin: the former starts right before the execvp system call, while the latter starts when the loader is invoked. This difference is constant and negligible in respect of our running times;

• applications under the control of Pin sometimes execute more instructions in the function dl_relocate_symbol.

Because Pin links with the application, more symbols exist in the executable, and the resolution may require more work. This happens only once for each executed symbol, and is also negligible for our benchmarks.

Since Pin only traces user mode events, we configured the PMU correspondingly. To quantify the impact of kernel events, we ran the benchmarks in both modes and we measured the number of retired instructions as well as the instruction mix (loads, stores, and jumps). Not surprisingly for an interpreter, the difference remains under one percentage point. For jumps, it is even below 0.05 percentage point.

The main loop of interpreters is identical on all architectures, even though we instructed the compiler to generate specialized code. The average variation of the number of executed instructions, due to slightly different releases of the operating system and libraries, is also on the order of 1 %. Finally, PMU and Pin also report counts within 1 %.

Experimental Results

Overview

Figures 4, 5 and 6 illustrate the branch misprediction rates measured in our experiments on respectively Python, Javascript and CLI interpreters. The branch misprediction rates are measured in MPKI, misprediction per kilo instructions. MPKI is generally considered as a quite illustrative metric for branch predictors, since it allows to get at a first glance a very rough estimation of the lost cycles per kiloinstructions: lost cycles KI = M P KI × average penalty. For the sake simplicity and for a rough analysis, we will assume on the considered architectures an average penalty of 20 cycles. Our measures clearly show that, on Nehalem, on most benchmarks, 12 to 16 MPKI are encountered, that is about 240 to 320 cycles are lost every 1 kiloinstructions. On the next processor generation Sandy Bridge, the misprediction rate is much lower: generally about 4 to 8 MPKI on Javascript applications for instance, i.e. decreasing the global penalty to 80 to 160 cycles every Kiloinstructions. On the most recent processor generation Haswell, the misprediction rate further drops to 0.5 to 2 MPKI in most cases, that is a loss of 10 to 40 cycles every kiloinstructions. Interestingly, the misprediction rates simulated assuming at TAGE + ITTAGE branch predictor scheme are in the same range as the misprediction rates measured on Haswell. This rough analysis illustrates that, in the execution time of interpreted applications, total branch misprediction penalty has gone from a major component on Nehalem to only a small fraction on Haswell.

The rough analysis presented above can be refined with models using performance monitoring counters. Intel [START_REF] Marusarz | How to tune applications using a top-down characterization of microarchitectural issues[END_REF] describes such methodology to compute wasted instruction issue slots in the processor front-end. We relied on Andi Kleen's implementation pmu-tools 5 and backported the formulas. Unfortunately, only Sandy Bridge and Haswell are supported. In the front-end, issue slots can be wasted in several cases: branch misprediction, but also memory ordering violations, self modifying code (SMC), and AVX- [START_REF] Davidson | Cint: a RISC interpreter for the C programming language[END_REF]. Correlation between MPKI and lost slots related events. We confirmed that AVX never occurs and SMC is negligible in our experiments. Moreover, apart a very few cases, the number of memory ordering violations is two to three orders of magnitude less than the number of branch mispredictions. Nearly all wasted issue slots can be attributed to branch mispredictions.

Figure 7 shows, for all our benchmarks, how MPKI relates to wasted slots. Lower MPKI correlates with fewer wasted slots. On average Haswell wastes 7.8 % of the instruction slots due to branch mispredictions, while Sandy Bridge wastes 14.5 %. Better branch prediction on Haswell results in 50 % fewer wasted slots.

This confirms that, on last generation Intel processors, Haswell, branch misprediction penalty has only a limited impact on interpreted applications execution time.

In the remainder of this section, we present a detailed analysis of the measures and simulations for each interpreter separately. We also debunk the folklore on the "hard to predict" branches in the interpreter loop.

Detailed Analysis

We present results for each interpreter (Tables 3,4,5). The left part reports general characteristics of the interpreter. For each benchmark, the tables show the number of executed bytecodes (in millions), the number of native instructions (in billions), the overall measured performance reported as IPC (instructions per cycle), the number of executed instructions per bytecode, the fraction of branch instructions, the fraction of indirect branch instructions, and finally the number of indirect branch instructions per bytecode.

The second half of the tables report on the performance of each architecture in terms of branch prediction (MPKI) for each benchmark on Nehalem, Sandy Bridge, Haswell, TAGE1 and TAGE2.

Generally all three interpreters run at quite high performance with median values of 1.5, 1.4, 1.2 IPC for respectively Python, Javascript and CLI on Nehalem, 1.7, 1.5 and 1.2 on Sandy Bridge, and 2.4, 2.4 and 2.2 on Haswell.

Our experiments clearly show that between three recent generations of Intel processors, the improvement on the branch prediction accuracy on interpreters is dramatic for Python and Javascript. Our simulations of TAGE and IT-TAGE show that, as long as the payload in the bytecode remains limited and do not feature significant amount of extra indirect branches, then the misprediction rate on the interpreter can be even become insignificant (less than 0.5 MPKI). For CLI, the results on Nehalem and Sandy Bridge are much more mitigated than Python and Javascript, with Sandy Bridge only reducing the misprediction rate by at most 25 % and often much less, e.g. on art, it even loses accuracy. Haswell, however, predicts much better, achieving results close to TAGE+ITTAGE.

To summarize, the predictability of branches, both indirect and conditional, should not be considered as an issue anymore for Javascript and Python interpreters.

Python

Python is implemented in C. The syntax of the Python language differs slightly between versions 2 and 3 (a converter is provided), and so does the bytecode definition. Still, both have a similar set of roughly 110 opcodes.

The dispatch loop is identical on Nehalem, Sandy Bridge and Haswell (with the exception of stack offsets), it consists in 24 instructions for bytecodes without arguments, and 6 additional instructions to handle an argument. These instructions check for exit and tracing conditions, loading the next opcode, accessing the jump table, and directing control to the corresponding address. A few instructions detect pending exceptions, and handle objects allocation/deallocation. Only one indirect branch is part of the dispatch loop.

Table 3 report our results with the Python interpreter. The performance (measured as IPC) on Nehalem and Sandy Bridge is fairly good, showing that no serious problem (such as cache misses or branch misprediction) is degrading performance. It is even better on Haswell, with a median value of 2.4 IPC, and up to 3.4 IPC.

It takes 120 to 150 instructions to execute a bytecode. Considering the overhead of the dispatch, about 100 instructions are needed to execute the payload of a bytecode. This rather high number is due to dynamic typing. A simple add must check the types of the arguments (numbers or strings), and even in the case of integers, an overflow can occur, requiring special treatment.

In a few cases, the number is much higher, as for the fastpickle or regex benchmarks. This is because they apply heavier processing implemented in native libraries for performance. In the case of fastpickle, the benchmark serializes objects by calling a dedicated routine.

There is generally a single indirect branch per bytecode. Values significantly larger than 1 are correlated with a high number of instructions per bytecode, revealing that the execution has left the interpreter main loop proper to execute a dedicated routine.

Figure 4 plots the MPKI for each benchmark and branch predictor. It is clear that the Sandy Bridge predictor significantly outperforms Nehalem's, and the same applies for Haswell and TAGE when compared to Sandy Bridge.

In practice, one can note that when the average payload is around 120 to 150 instructions and there are no (or very few) indirect branches apart the main switch, i.e., ind bc ≤ 1.02, then TAGE+ITTAGE predicts the interpreter quasiperfectly. When the average payload is larger or some extra indirect branches are encountered then misprediction rate of TAGE+ITTAGE becomes higher and may become in the same order as the one of Haswell.

Javascript

SpiderMonkey Javascript is implemented in C++. We compiled it without JIT support, and we manually removed the detection of Labels as Values. The bytecode consists in 244 entries. The dispatch loop consists in 16 instructions, significantly shorter that Python.

Table 4 reports the characteristics of the Javascript interpreter. With the exception of code-load in octane, and parse-financial and stringify-tinderbox in kraken, indirect branches come from the switch statement. These benchmarks also have a outstanding number of instructions per bytecode. Excluding them, it takes on average in the order of 60 instructions per bytecode.

Table 4 also reports on the performance of the branch predictors, and Figure 5 illustrates the respective MPKI. As for Python, Haswell and TAGE consistently outperform Sandy Bridge, which also outperforms Nehalem.

As for the Python interpreters, with the exception of three outliers, TAGE predict quasi perfectly the interpreter.

CLI

The CLI interpreter is written in standard C, hence dispatch is implemented with a switch statement. The internal IR consists in 478 opcodes. This IR resembles the CLI bytecode from which it is derived. CLI operators are not typed, the same add (for example) applies to all integer and floating point types. The standard, though, requires that types can be statically derived to prove the correctness of the program before execution. The interpreter IR specializes the operators with the computed types to remove some burden from the interpreter execute loop. This explains the rather large number of different opcodes. As per Brunthaler's definition [START_REF] Brunthaler | Virtual-machine abstraction and optimization techniques[END_REF], the CLI interpreter is a low abstraction level interpreter.

The dispatch loop consists in only seven instructions, illustrated on Figure 8. This is possible because each opcode is very low level (strongly typed, and derived from C operators), and there is no support for higher abstractions such as garbage collection, or exceptions.

Table 5 reports on the characteristics of the CLI interpreter and the behavior of the predictors. The number of speculative indirect jumps is between 1.01 and 1.07 bytecodes. In fact, most of the code is interpreted, even libraries such as libc and libm. Execution goes to native code at a cutpoint similar to a native libc does a system call. The short loop is also the reason why the fraction of indirect branch instructions is higher than Javascript or Python. The compiler could not achieve stack caching 6 , probably due to the limited number of registers. Figure 8 illustrates the x86 assembly code of the dispatch loop, as well as entry for the ADD bytecode. The dispatch consists in seven instructions. Two of them (lines 2 and 3) perform the useless range check that the compiler could not remove. Note the instructions at line 9-11: the compiler chose to replicate the code typically found at the top of the infinite loop and move it at the bottom of each case entry (compare with Figure 1). Across all benchmarks, 21 instructions are needed to execute one bytecode. Nehalem, Sandy Bridge and TAGE are ranked in the same order as for the other two interpreters. Hawell's predictor is comparable to TAGE, with occasional wins for TAGE (2.6 vs 0.21 MPKI on 177.mesa) and for Haswell (0.2 vs 0.38 on 179.art).

However, with the CLI interpreter, the accuracy of TAGE +ITTAGE is particularly poor on vpr and crafty, i.e. misprediction rate exceeds 1 MPKI. We remarked that in these cases the accuracy of the smaller ITTAGE (TAGE2) is much lower than the one with the larger ITTAGE (TAGE1). Therefore the relatively low accuracy seems to be associated with interpreter footprint issues on the ITTAGE predictor. To confirm this observation, we run an extra simulation TAGE3 where the ITTAGE predictor is 50 KB. A 50KB ITTAGE predictor allows to reduce the misprediction rate to the "normal" rate except for crafty which would still need a larger ITTAGE. The very large footprint required by the interpreter on the ITTAGE predictor is also associated with the huge number of possible targets (478) in the main switch of the interpreter.

Folklore on "hard to predict" branches

The indirect branch of the dispatch loop in each interpreter is generally considered as the one that is very hard to predict. Simulation allows us to observe the individual behavior of specific branch instructions. We measured the misprediction ratio of the indirect branch of the dispatch loop in each interpreter. Moreover the source code of Python refers to two "hard to predict" branches. The first is the indirect branch that implements the switch statement. The second comes for the macro HAS_ARG that checks whether an opcode has an argument. For Python, we also considered this conditional branch. Table 6 reports the misprediction numbers for these branches for all benchmarks for the three sizes of ITTAGE predictors, 6 KB, 12 KB and 50 KB. On Python, the indirect jumps are most often very well predicted for most benchmarks, even by the 6 KB ITTAGE. However, in several cases the prediction of indirect jump is poor (see for example the Python chaos, django-v2, formatted-log, go). However, these cases except go are in practice near perfectly predicted by the 12 KB configuration: that is in practice the footprint of the Python application on the indirect jump predictor is too large for the 6 KB configuration, but fits the 12 KB configuration. go needs an even larger predictor as illustrated by the results on the 50 KB configuration. HAS_ARG turns out to be very easily predicted by the conditional branch predictor TAGE at the exceptions of the same few outliers with 1% to 4% mispredictions.

For Javascript, the indirect branch also appears as quite easy to predict with misprediction rates generally lower than 1% with the 12 KB ITTAGE. More outliers than for Python are encountered, particularly code-load and type_script. However these outliers are all amenable to low misprediction rates with a 50 KB predictor at the exception of code-load. However, code-load executes more than 4,000 instructions per bytecode on average (see Table 4) and therefore the predictability of the indirect jump in the interpreter dispatch loop has a very limited impact on the overall performance.

With the CLI interpreter, the main indirect branch suffers from a rather high misprediction rate when executing vpr and crafty (and bzip2 to some extent) with a 12 KB IT-TAGE. But a 50 KB ITTAGE predictor strictly reduces this misprediction rate except for crafty which would need an even larger ITTAGE predictor.

Therefore the folklore on the unpredictability of the indirect branch in the dispatch loop is rather unjustified: this indirect branch is very predictable provided the usage of a large enough efficient indirect jump predictor.

Other Related Work

This paper is about the interaction of interpreters with branch predictors. The most relevant work on branch prediction is covered by Section 3. The overhead of interpreters compared to native code derives mostly from two sources: the management of the evaluation stack and the dispatch loop.

Very recent work by Savrun-Yenic ¸eri et al. [START_REF] Savrun-Yenic ¸eri | Efficient interpreter optimizations for the JVM[END_REF] still references the original work of Ertl and Gregg [START_REF] Ertl | Optimizing indirect branch prediction accuracy in virtual machine interpreters[END_REF]. Their approach, however, is very different: they consider host-VM targeted interpreters, i.e. interpreters for languages such as Python or Javascript implemented on top of the Java VM. Performance results are difficult to compare with ours.

Vitale and Abdelrahman [START_REF] Vitale | Catenation and specialization for Tcl virtual machine performance[END_REF] eliminate the dispatch overhead with a technique called catenation. It consists in copying and combining at run-time sequences of native code produced when the interpreter was compiled. Half the bench- McCandless and Gregg [START_REF] Mccandless | Compiler techniques to improve dynamic branch prediction for indirect jump and call instructions[END_REF] propose to optimize code at the assembly level to eliminate interferences between targets of the indirect branch. Two techniques are developed: forcing alignment, and reordering entries. The hardware used for experiments is a Core2 processor. We claim that modern branch predictors are quite insensitive to target placement.

Stack Caching and Registers

With the notable exception of the Dalvik virtual machine [START_REF] Ehringer | The Dalvik virtual machine architecture[END_REF], most current interpreters perform their computations on an evaluation stack. Values are stored in a data structure which resides in memory (recall Figure 1 for illustration). Ertl proposed stack caching [START_REF] Ertl | Stack caching for interpreters[END_REF] to force the top of the stack into registers. Together with Gregg, they later proposed to combine it with dynamic superinstructions [START_REF] Ertl | Combining stack caching with dynamic superinstructions[END_REF] for additional performance. Stack caching is orthogonal to the behavior of the branch predictor. While it could decrease the number of cycles of the interpreter loop, and hence increase the relative impact of a misprediction, this data will typically hit in the L1 cache and aggressive out-of-order architectures are less likely to benefit, especially for rather long loops, and the already reasonably good performance (IPC). Register allocators have a hard time keeping the relevant values in registers 7 because of the size and complexity of the main interpreter loop. Stack caching also adds significant complexity to the code base.

As an alternative to stack caching, some virtual machines are register-based. Shi et al. [START_REF] Shi | Virtual machine showdown: Stack versus registers[END_REF] show they are more efficient when sophisticated translation and optimizations are applied. This is orthogonal to the dispatch loop.

Superinstructions and Replication

Sequences of bycodes are not random. Some pairs are more frequent than others (e.g. a compare is often followed by a branch). Superinstructions [START_REF] Piumarta | Optimizing direct threaded code by selective inlining[END_REF] consist in such sequences of frequently occurring tuples of bytecode. New bytecodes are defined, whose payloads are the combination of the payloads of the tuples. The overhead of the dispatch loop is unmodified but the gain comes from a reduced number of iterations of the loop, hence a reduced average cost. Ertl and Gregg [START_REF] Ertl | Optimizing indirect branch prediction accuracy in virtual machine interpreters[END_REF] discuss static and dynamic superinstructions.

Replication, also proposed by Ertl and Gregg, consists in generating many opcodes for the same payload, specializing each occurrence, in order to maximize the performance of 7 Even with the help of the GNU extension register asm, or manually allocating a few top-of-stack elements in local variable. branch target buffers. Modern predictors no longer need it to capture patterns in applications.

Jump Threading

We discuss jump threading in general terms in previous sections. To be more precise, several versions of threading have been proposed: token threading (illustrated in Figure 2), direct threading [START_REF] Bell | Threaded code[END_REF], inline threading [START_REF] Piumarta | Optimizing direct threaded code by selective inlining[END_REF], or context threading [START_REF] Berndl | Context threading: A flexible and efficient dispatch technique for virtual machine interpreters[END_REF]. All forms of threading require extensions to ANSI C. Some also require limited forms of dynamic code generation and walk away from portability and ease of development.

Conclusion

Despite mature JIT compilation technology, interpreters are very much alive. They provide ease of development and portability. Unfortunately, this is at the expense of performance: interpreters are slow. Many studies have investigated ways to improve interpreters, and many design points have been proposed. But many studies go back when branch predictors were not very aggressive, folklore has retained that a highly mispredicted indirect jump is one of the main reasons for the inefficiency of switch-based interpreters.

In this paper, we shed new light on this claim, considering current interpreters and state-of-the-art branch predictors. We show that the accuracy of branch prediction on interpreters has been dramatically improved over the three last Intel processor generations. This accuracy on Haswell, the most recent Intel processor generation, has reached a level where it can not be considered as an obstacle for performance anymore. We have also shown that this accuracy is on par with the one of the literature state-of-the-art ITTAGE. While the structure of the Haswell indirect jump predictor is undisclosed, we were able to confirm with simulations of ITTAGE that the few cases where the prediction accuracy is relatively poor are due to footprint issues on the predictor and not inherent to the prediction scheme.

Figure 1 .

 1 Figure 1. Main loop of naive interpreter

  v o i d * l a b e l s [ ] = { &&ADD, &&SUB . . . } ; . . . g o t o * l a b e l s [ * vpc + + ] ; ADD: x = pop ( s t a c k ) ; y = pop ( s t a c k ) ; p u s h ( s t a c k , x+y ) ; g o t o * l a b e l s [ * vpc + + ] ; SUB : . . . g o t o * l a b e l s [ * vpc + + ] ;

Figure 2 .

 2 Figure 2. Token threading, using a GNU extension

2 Figure 3 .

 23 Figure 3. Speedups in Python

Figure 4 .Figure 5 .Figure 6 .

 456 Figure 4. Python MPKI for all predictors

5

  https://github.com/andikleen/pmu-tools

Figure

  Figure 7. Correlation between MPKI and lost slots

Figure 8 .

 8 Figure 8. Dispatch loop of the CLI interpreter (+ADD)

Table 1 .

 1 Benchmarks

	Python	regex v8	crypto
	call method	richards	deltablue
	call method slots	silent logging	earley-boyer
	call method unknown simple logging	gbemu
	call simple	telco	mandreel
	chaos	unpack sequence	navier-stokes
	django v2	Javascript (kraken)	pdf
	fannkuch	ai-astar	raytrace
	fastpickle	audio-beat-detection	regexp
	fastunpickle	audio-dft	richards
	float	audio-fft	splay
	formatted logging	audio-oscillator	typescript
	go	imaging-darkroom	zlib
	hexiom2	imaging-desaturate	CLI
	json dump v2	imaging-gaussian-blur	164.gzip
	json load	json-parse-financial	175.vpr
	meteor contest	json-stringify-tinderbox 177.mesa
	nbody	crypto-aes	179.art
	nqueens	crypto-ccm	181.mcf
	pathlib	crypto-pbkdf2	183.equake
	pidigits	crypto-sha256-iterative	186.crafty
	raytrace	Javascript (octane)	188.ammp
	regex compile	box2d	197.parser
	regex effbot	code-load	256.bzip2

Table 2 .

 2 Branch predictor parameters

	Parameter	TAGE	ITTAGE 2 ITTAGE 1
	min history length	5	2	2
	max history length	75	80	80
	num tables (N)	5	8	8
	num entries table T 0	4096	256	512
	num entries tables T 1 -T N -1	1024	64	128
	storage (kilobytes)	8 KB	6.31 KB	12.62 KB
	4.2.1 Existing Hardware -Performance Counters
	Branch prediction data is collected from the PMU (perfor-
	mance monitoring unit) on actual Nehalem (Xeon W3550
	3.07 GHz), Sandy Bridge (Core i7-2620M 2.70 GHz), and
	Haswell (Core i7-4770 3.40 GHz) architectures running
	Linux. Both provide counters for cycles, retired instructions,
	retired branch instructions, and mispredicted branch instruc-
	tions. We relied on Tiptop [26] to collect data from the PMU.
	Events are collected per process (not machine wide) on an
	otherwise unloaded workstation. Only user-land events are
	collected (see also discussion in Section 4.2.3).

Table 3 .

 3 Python characteristics and branch prediction for Nehalem (Neh.), Sandy Bridge (SB), Haswell (Has.) and TAGE

	benchmark	Mbc	Gins		IPC		ins/bc	br	ind	ind/bc			MPKI
				Neh.	SB Has.					Neh.	SB Has. TAGE 1 TAGE 2
	call method	6137	771	1.84	2.2	2.93	125.6	20 % 0.8 %	1.01	4.8 0.6	0.1	0.067	0.067
	call method slots	6137	766	1.87 2.18	2.90	124.8	20 % 0.8 %	1.02	5.9 0.7	0.1	0.068	0.068
	call method unknown	7300	803	1.99 2.22	2.88	110.0	20 % 0.9 %	1.00	4.2 0.3	0.1	0.058	0.058
	call simple	5123	613	1.89 2.33	3.12	119.6	19 % 0.8 %	1.00	4.1 0.5	0.1	0.086	0.086
	chaos	1196	162	1.34 1.55	2.21	135.4	22 % 0.8 %	1.07	18.4 4.4	1.8	0.680	2.548
	django v2	1451	332	1.22 1.44	2.10	228.7	21 % 0.6 %	1.33	15.9 3.9	1.5	0.529	1.829
	fannkuch	7693	747	1.52 1.67	2.44	97.1	23 % 1.3 %	1.23	18.4 6.1	0.9	0.578	0.592
	fastpickle	34	351	1.62 1.89	2.63	10277 22 % 0.5 %	54	19 2.6	1.4	2.258	2.290
	fastunpickle	25	278	1.47 1.77	2.06	11320 22 % 0.8 %	91	16.5	3	1.7	2.365	2.673
	float	1280	180	1.67 1.73	2.27	140.6	22 % 0.8 %	1.15	12	3	1.5	0.364	0.365
	formatted logging	750	125	0.89 1.13	1.74	166.9	22 % 0.7 %	1.20	17.5 5.4	2.8	0.633	4.220
	go	2972	344	1.43	1.6	2.20	115.7	21 % 0.9 %	1.01	14 5.2	2.4	1.121	1.979
	hexiom2	33350	3674	1.72 1.86	2.52	110.2	21 % 1.0 %	1.08	11.9 2.9	0.8	0.563	0.832
	json dump v2	5042	1656	1.48 1.62	2.44	328.5	23 % 0.8 %	2.61	17.2 3.5	0.6	0.827	0.859
	json load	195	271	1.57 1.81	2.50	1391 26 % 0.5 %	6.76	15.7 3.3	2.1	3.074	3.198
	meteor contest	868	106	1.36 1.52	1.89	122.3	22 % 0.9 %	1.05	16.7 6.9	5.5	3.507	3.519
	nbody	2703	184	1.78 1.73	2.45	68.2	23 % 1.5 %	1.00	13.8 5.9	2.1	0.700	0.701
	nqueens	1296	152	1.34 1.46	2.35	117.0	22 % 0.9 %	1.06	16.5 3.9	0.9	0.549	0.549
	pathlib	836	188	0.79 1.08	1.88	225.3	22 % 0.6 %	1.45	16.5 4.7	1.2	0.397	0.633
	pidigits	94	223	2.33 2.37	2.76	2366	7 % 0.1 %	1.42	1.2 0.5	0.4	0.356	0.363
	raytrace	4662	766	1.40 1.66	2.21	164.3	21 % 0.7 %	1.12	15.2 3.6	1.8	0.577	1.017
	regex compile	1938	214	1.33 1.52	2.17	110.6	21 % 1.0 %	1.05	15.1 5.3	2.1	1.588	2.257
	regex effbot	7	52	2.45 2.53	3.35	6977 22 % 2.1 %	146	6.1 0.1	0.0	0.026	0.027
	regex v8	31	37	1.79 1.79	2.39	1182 22 % 1.7 %	21	9.8 1.1	0.6	0.506	0.534
	richards	961	108	1.39 1.59	2.27	111.9	21 % 0.9 %	1.02	13.2 5.8	1.8	0.824	1.518
	silent logging	353	53	1.75 1.83	2.59	148.8	21 % 0.7 %	1.08	10.3 3.7	0.4	0.035	0.035
	simple logging	731	120	0.93 1.14	1.78	164.4	22 % 0.7 %	1.19	17.4 5.3	2.8	0.669	4.748
	telco	34	6	1.28 1.28	2.24	174.6	21 % 1.3 %	2.35	15.6 5.7	1.5	1.143	1.150
	unpack seq	966	38	1.90 2.04	2.86	39.5	21 % 2.6 %	1.01	8.9 4.3	2.2	0.056	0.057
	average										12.8 3.5	1.4	0.8	1.3

Table 4 .

 4 Javascript characteristics and branch prediction for Nehalem (Neh.), Sandy Bridge (SB), Haswell (Has.) and TAGE

	benchmark	Mbc	Gins		IPC		ins/bc	br	ind ind/bc			MPKI
				Neh.	SB Has.					Neh.	SB Has.	T1	T2
							kraken
	ai-astar	5713	296	1.55 1.55	2.36	51.8 20 % 1.9 %	1.002	17.5	6.4	0.0 0.01 0.01
	audio-beat-detection	4567	195	1.42 1.59	2.56	42.7 19 % 2.3 %	1.002	13.4	6.6	1.5 0.14 0.16
	audio-dft	3311	169	1.62 1.58	2.57	51.0 20 % 2.0 %	1.004	14.5	4.3	1.1 0.01 0.01
	audio-fft	4459	189	1.42 1.59	2.65	42.4 19 % 2.4 %	1.002	13.5	6.7	1.3 0.12 0.12
	audio-oscillator	2541	162	1.69 1.61	2.61	63.8 21 % 1.6 %	1.033	10.5	5.0	1.0 0.01 0.01
	imaging-darkroom	4387	234	1.40 1.33	2.31	53.3 20 % 1.9 %	1.022	14.7	9.3	2.1 0.07 0.08
	imaging-desaturate	8117	368	1.71 1.73	2.72	45.3 19 % 2.2 %	1.007	7.4	4.1	1.2 0.01 0.01
	imaging-gaussian-blur	24490	1278	1.60 1.59	2.70	52.2 19 % 1.9 %	1.060	15.8	6.6	1.1 0.17 0.17
	json-parse-financial	0.12	6	1.77 2.15	2.53	50000 21 % 1.2 %	569	17.3	1.1	1.0 1.76 1.77
	json-stringify-tinderbox	0.30	4	2.09 2.27	2.84	13333 24 % 0.2 %	23.7	12.0	1.3	1.2 1.71 1.71
	crypto-aes	1679	68	1.41 1.40	2.51	40.5 17 % 2.5 %	1.008	15.0	9.3	1.8 0.29 2.13
	crypto-ccm	1034	43	1.40 1.39	2.31	41.6 17 % 2.4 %	1.016	14.7	9.4	2.9 1.07 1.62
	crypto-pbkdf2	3592	139	1.29 1.34				2.6 %	1.006	14.4	9.4	2.3 0.71 1.24
	crypto-sha256-iterative	1136	45	1.36 1.41	2.54	39.6 16 % 2.5 %	1.011	13.9	8.5	2.2 0.87 1.01
							octane
	box2d	1958	131	1.26 1.30	2.01	66.9 20 % 1.6 %	1.016	16.5	9.2	3.2 1.64 2.46
	code-load	2.8	12	1.31 1.33	1.70	4286 21 % 0.3 %	14.3	13.0	5.5	5.0 4.44 4.53
	crypto	39480	1543	1.34 1.20	2.91	39.1 17 % 2.6 %	1.001	15.4 13.3	0.2 0.11	0.2
	deltablue	9443	824	1.43 1.46	2.13	87.3 21 % 1.1 %	1.012	11.1	5.7	2.0 0.13 0.44
	earley-boyer	12113	819	1.38 1.46	2.19	67.6 20 % 1.6 %	1.072	13.8	6.5	1.4 0.48 1.15
	gbemu	1928	122	1.60 1.65	2.23	63.3 20 % 1.7 %	1.092	13.0	5.4	1.5 0.37 0.53
	mandreel	3582	165	1.60 1.57	2.36	46.1 18 % 2.4 %	1.090	13.9	6.2	2.3 0.74 1.29
	navier-stokes	10586	563	1.61 1.54	2.77	53.2 20 % 1.9 %	1.037	12.1	6.1	1.0 0.01 0.01
	pdf	977	54	1.67 1.71	2.66	55.3 19 % 2.0 %	1.074	14.1	3.9	0.7 0.37 0.54
	raytrace	4984	482	1.35 1.34	1.99	96.7 21 % 1.1 %	1.100	14.7	5.7	2.2 0.99 2.47
	regexp	595	75	1.67 1.78	2.34	126.1	21 % 1.2 %	1.561	10.7	1.7	1.0 0.85	0.9
	richards	12523	1029	1.38 1.25	2.26	82.2 21 % 1.3 %	1.000	12.4	8.8	1.9 0.42 0.71
	splay	783	83	1.50 1.57	2.09	106.0	19 % 1.1 %	1.152	10.8	3.3	1.4 0.79 1.13
	typescript	1263	120	1.26 1.31	1.85	95.0 20 % 1.1 %	1.079	14.2	6.5	3.0 2.64 3.86
	zlib	41051	2106	1.47 1.45	2.47	51.3 18 % 2.2 %	1.138	13.5	8.0	2.0 0.46 1.40
	average										13.6	6.3	1.7	0.7	1.1

Table 5 .

 5 CLI characteristics and branch prediction for Nehalem (Neh.), Sandy Bridge (SB), Haswell (Has.) and TAGE

	benchmark	Gbc	Gins		IPC		ins/bc	br	ind	ind/bc			MPKI
				Neh.	SB Has.					Neh.	SB Has.	T1	T2	T3
	164.gzip	78.8 1667.2	1.20 1.23	2.55	21.2	23 % 4.8 %	1.02	18.7 14.5	0.5	0.64	1.58 0.62
	175.vpr	18.4	400.9	0.97	1	1.80	21.8	23 % 4.7 %	1.03	24.8 21.9	6.3	6.49 13.62 1.08
	177.mesa	118.6 3177.1	1.32	1.3	2.13	26.8	23 % 4.0 %	1.07	13.8 11.1	2.6	0.21	0.59 0.20
	179.art	4.7	58.5	1.64 1.49	2.70	12.5	26 % 8.0 %	1.00	7.3	9.3	0.2	0.38	0.38 0.37
	181.mcf	13.3	181.2	1.13 1.19	2.19	13.7	25 % 7.4 %	1.01	17.5 15.1	1.1	1.33	2.09 1.08
	183.equake	40.5	726.1	1.09 1.09	2.34	17.9	24 % 5.7 %	1.02	20.8	19	1.7	0.47	0.68 0.43
	186.crafty	35.8 1047.3	1.02 1.03	1.42	29.2	22 % 3.5 %	1.04	21.1 19.2	11.6 11.87 16.31 4.01
	188.ammp	91.3 1665.9	1.15 1.24	2.18	18.3	24 % 5.7 %	1.04	19.5 14.4	2.9	0.39	1.14 0.30
	197.parser	12.6	447.5	1.19 1.24	2.18	35.4	22 % 3.0 %	1.06	14.4 10.8	1.4	1.01	2.75 0.70
	256.bzip2	28.3	460.8	1.16 1.32	2.29	16.3	24 % 6.2 %	1.02	17.5 12.7	1.6	1.55	2.15 0.44
	average										17.5 14.8	3.0	2.4	4.1	0.9
	marks, however, run slower, because of the induced code
	bloat, and the instruction cache behavior degradation. They
	report the switch dispatch to be 12 instructions and 19 cy-
	cles on an UltraSparc-III processor, and on average 100 cy-
	cles per bytecode for the factorial function written in Tcl.

Table 6 .

 6 (IT)TAGE misprediction results for "hard to predict" branch, TAGE 1, TAGE 2 and TAGE 3 (all numbers in %)

	Python		indirect		ARG		Javascript				CLI
		IT 1	IT 2	IT3 TAGE		IT 1	IT 2	IT3		IT 1	IT 2	IT 3
	call-meth	0.827	0.827 0.827	0.000	ai-astar	0.012	0.012	0.012	164.gzip	0.612	2.698 0.569
	call-meth-slots	0.827	0.827 0.827	0.000	a-beat-detec.	0.064	0.130	0.063	175.vpr	12.527 27.812 0.905
	call-meth-unk	0.626	0.626 0.626	0.000	audio-dft	0.003	0.003	0.003	177.mesa	0.050	1.026 0.019
	call-simple	0.991	0.991 0.990	0.000	audio-fft	0.064	0.064	0.064	179.art	0.077	0.083 0.075
	chaos	0.165 21.362 0.163	3.456	a-oscillator	0.001	0.001	0.001	181.mcf	1.020	1.994 0.681
	django-v2	0.308 22.421 0.016	0.372	i-darkroom	0.023	0.083	0.023	183.equake	0.185	0.541 0.113
	fannkuch	0.652	0.718 0.630	0.001	i-desaturate	0.000	0.000	0.000	186.crafty	32.405 45.311 9.688
	fastpickle	0.478	0.634 0.397	0.042	i-gaussian-blur	0.062	0.062	0.062	188.ammp	0.382	1.752 0.222
	fastunpickle	0.723	3.730 0.547	0.060	j-parse-financial	0.317	0.572	0.163	197.parser	1.881	7.939 0.786
	float	0.008	0.010 0.005	0.821	j-s-tinderbox	2.460	2.753	2.043	256.bzip2	2.190	3.102 0.470
	formatted-log	0.136 48.212 0.021	1.216	c-aes	0.323	7.725	0.288
	go	4.407 13.521 1.728	0.980	c-ccm	3.429	5.666	0.252
	hexiom2	1.749	4.510 0.571	1.042	c-pbkdf2	0.100	2.039	0.094
	json-dump-v2	0.015	0.200 0.001	0.841	c-sha256-it.	0.305	0.827	0.085
	json-load	2.580	3.676 0.057	1.630	box2d	7.362 12.618	0.936
	meteor-contest	0.460	0.558 0.281	0.583	code-load	31.662 36.134 24.257
	nbody	0.002	0.003 0.002	0.758	crypto	0.166	0.494	0.108
	nqueens	0.518	0.526 0.515	0.357	deltablue	0.380	3.042	0.032
	pathlib	0.104	3.635 0.027	0.965	earley-boyer	0.902	5.264	0.748
	pidigits	1.719	3.169 0.719	2.958	gbemu	1.690	2.688	0.569
	raytrace	0.873	6.691 0.228	3.861	mandreel	2.356	4.742	0.366
	regex-compile	9.852 16.284 0.567	0.589	navier-stokes	0.024	0.029	0.022
	regex-effbot	1.311	1.608 0.848	0.177	pdf	0.506	1.424	0.351
	regex-v8	1.678	2.374 0.918	0.112	raytrace	6.100 20.337	0.439
	richards	0.420	6.775 0.337	1.602	regexp	0.427	0.813	0.324
	silent-logging	0.316	0.321 0.308	0.004	richards	0.544	2.769	0.474
	simple-logging	0.030 53.552 0.021	1.197	splay	1.016	4.509	0.895
	telco	0.264	0.342 0.172	0.044	typescript	18.291 29.630	4.100
	unpack-sequence 0.027	0.029 0.025	0.001	zlib	2.079	6.771	0.228

http://www.jilp.org/jwac-2/

http://code.google.com/p/octane-benchmark

http://krakenbenchmark.mozilla.org/kraken-1.1/

despite a number of attempts...

Acknowledgment

This work was partially supported by the European Research Council Advanced Grant DAL No 267175.